Access protocols

How files enter Cylo without handing plaintext to the hosting layer.

CYLO1 envelope

Generic JSON. Algorithm, IV, and ciphertext only. Filenames are a separate sealed blob with AAD cylo-name.

{
  "fmt": "CYLO1",
  "v": 1,
  "alg": "A256GCM",
  "mode": "ingest" | "vault" | "share",
  "iv": "<base64 12-byte nonce>",
  "ct": "<base64 ciphertext + GCM tag>",
  "wrap": { "iv": "...", "ct": "..." }  // vault mode only
}

Rust core

Operators, shares, quotas, and ingest are served by Cylo Core — a memory-safe Rust service. It stores CYLO1 envelopes only. Passphrases and share fragment keys never reach it.

GET  /health              // lang=rust, backend=sqlite|postgres
POST /v1/ingest           // hashed cylo_ API keys
GET  /v1/vault            // wrapped master, KDF salt — no plaintext
POST /v1/shares/open      // independent CYLO1 copy, consume-on-decrypt

The browser UI and Better Auth stay on the web tier. The core binds locally and requires a workspace secret on operator calls so envelopes are never exposed on a public socket.

HTTPS ingest

POST /api/v1/ingest with a Bearer API key. Body is already sealed. Disk and database snapshots do not contain names or file bodies. Include contentType so later view/download uses the right MIME.

curl -X POST "$ORIGIN/api/v1/ingest" \
  -H "Authorization: Bearer cylo_…" \
  -H "Content-Type: application/json" \
  -d '{"kind":"text","contentType":"text/plain","encName":"...","envelope":{...},"byteSize":1204}'

Invite protocol

Closed operator roster after the first owner founds the workspace. Invites admit people — they never share vault material.

cyloinv_<48 hex chars>     // 192-bit secret, shown once
SHA-256(token)             // only the hash is stored
email? + role + expiry     // optional bind; default 7 days
max_uses 1–10              // email-bound invites force 1

Bootstrap. The first signed-in operator creates the workspace and becomes owner. After that, console access requires membership.

Issue. Owners invite admins and operators. Admins invite operators. Cylo returns the raw token once; later lists show only a prefix.

Accept. Open /invite/cyloinv_… or paste the token. Email-bound invites must match the authenticated address (case-insensitive). Use count is incremented atomically so two people cannot share a single-use token.

Isolation. Joining creates a roster row. Each member still creates their own sealed vault. There is no wrapped-master sharing, no org-wide DEK, and no path for an admin to read another operator’s envelopes.

Revoke / remove. Pending tokens can be revoked. Removing a member drops roster access immediately; their stored envelopes stay sealed under their user id. Owners and admins can delete those envelopes to reclaim quota without decrypting them. Members can delete their own files from the vault.

Share protocol

Re-seal a copy for a teammate or an anonymous recipient without unwrapping the original vault.

GET  /s/cyloshare_<48 hex>[#<url-safe key>]
mode=share                 // independent CYLO1 copy
SHA-256(token)             // only the hash is stored
audience=link | email | roster
                           // email requires a matching sign-in
                           // roster opens from Team files with a passphrase
                           // teammates open new uploads without this copy
optional PBKDF2 wrap       // required for roster; optional for link/email

Create. The owner decrypts in the browser, generates a one-time AES-256-GCM share key, and uploads a new envelope. The vault passphrase never goes in the link.

Anonymous. Anyone with the full URL can open it. The key lives in the fragment after #, so it is not sent to Cylo and does not appear in server logs.

Named recipient. Bind an email (roster or outside). Fetching the envelope requires a matching signed-in address. They still need the fragment key or passphrase to decrypt.

Team roster. Optional extra copy for everyone on the workspace. A passphrase wrap is required. Teammates open it from Vault → Team files. New uploads already open after they unlock — no extra password — so this audience is only for a resealed copy.

Passphrase. Optional for link and email shares. Wraps the share key with PBKDF2 so chat apps that strip # fragments still work. Required for roster shares. Send the passphrase on a second channel.

Limits. Expiry 1 / 7 / 30 days. Max opens No limit / 1 / 5 / 20 / 100. Revoke anytime. Deleting the original file or destroying the vault drops its shares.

Other paths

Initial setup. Until a workspace exists, Cylo opens on a first-run configuration page. The first account becomes owner. There is no default administrator login.

Web console. Drag-and-drop in the vault. Encryption happens before the upload request.

Sealed share. Vault → Share creates a CYLO1 copy for a link, a named email, or the team roster. Recipients open /s/cyloshare_… (link/email) or Vault → Team files (roster) without vault access.

Sealed export. Download decrypts locally, or keep the stored envelope as an opaque CYLO1 document for offline handoff.

Health. GET /api/v1/health returns a JSON probe for shippers. No auth, no data.

Shipper simulator

Paste the API key and ingest key issued in Access keys, then POST a sealed file through the public ingest protocol.