ctx.cat

ctx.cat Security Model

ctx.cat private shares are zero-knowledge by default: content and metadata are encrypted before upload, and decrypt keys remain in URL fragments.

Signing

Hosted ctx.cat does not sign plaintext user content. CTX_CAT_ENABLE_REMOTE_SIGNING defaults to disabled, so /signing/sign is only for local, self-hosted, or explicitly trusted deployments.

Author signatures should be created by local clients such as ctxcat or ctxcat-mcp. Server provenance receipts cover hashes only:

Verification States

not published for that user at verification time.

allowed org.

failed.

match the encrypted metadata.

Ownership

Private owner URLs use #key=<decryptKey>&owner=<ownerCapability>. Legacy reader URLs using #<decryptKey> still load. The owner capability authorizes encrypted edits, deletion, expiration changes, and owner-marked encrypted comments.

Public and unlisted pages use #owner=<ownerCapability> for edits, deletes, and owner-marked comments. Their content is plaintext by design.

Client-authoritative create capabilities

The v1 create route separates allocation from authorization. A client chooses a random 128-bit record ID and a random 256-bit owner capability. Create sends only a domain-separated SHA-256 commitment over the decoded owner-capability bytes. The raw owner capability is rejected on create and is sent only for an authenticated reconciliation, edit, or delete. Private reader keys never leave the client.

Commitments are authorization verifiers, not replay identifiers. The service does not classify requests, store a replay fingerprint, or expose a commitment in responses. Logs and errors must contain only the record ID, status, and a generic message; capability and commitment headers must be redacted.

The commitment does not make a weak capability safe. Clients must generate the full 32 random bytes, encode them as canonical unpadded base64url, and compare reconciliation content locally before treating an ambiguous create as success. Private reconciliation streams ciphertext rather than placing base64 in JSON. Clients should hash that stream incrementally, enforce the declared size and a deadline, and compare it with the already prepared ciphertext and encrypted metadata digests before accepting an ambiguous create.