Skip to content

Whitepaper

Local intelligence. Encrypted continuity.

Version 1.0
Date 3 August 2026
Status Pre-production
Scope Product + security architecture

Tilde is a local-first AI workspace. Its defining choice is not a privacy setting but an architectural boundary: the model and the canonical working copy of a conversation live on the user’s device. The cloud is used for authentication, encrypted continuity, and deliberately requested network tools—not as the default home of the user’s intelligence.

This paper describes Tilde’s implemented local and cloud continuity architecture. It explains what Tilde protects, which metadata remains visible, how a new device earns access, how recovery works when every trusted device is unavailable, and where the system’s trust boundaries sit.

Local inference + source of truth
Encrypted before storage or transport
Synchronized opaque records + minimal metadata

Abstract

Tilde encrypts durable conversation content with a randomly generated 256-bit personal vault key using libsodium’s XSalsa20-Poly1305 authenticated encryption. Each trusted device has an independent Curve25519 keypair and receives the vault key inside a sealed, device-bound envelope. A separate 160-bit Recovery Key can open a recovery envelope after Email OTP authentication when no trusted device is available. Supabase coordinates accounts, devices, opaque key envelopes, and an ordered change ledger, but it does not receive the plaintext vault key or synchronized content.

The Room database, with durable content encrypted at the field level, remains the source of truth. Local writes commit immediately and add a mutation to a durable outbox in the same transaction. Small, idempotent REST batches synchronize those ciphertext records through a narrowly granted RPC surface. No WebSocket, polling loop, or generalized replication service is required for correctness.

Table of Contents

  1. The Product Boundary
  2. Design Principles
  3. Threat Model
  4. Encrypted Local Foundation
  5. The Personal Vault
  6. Trusted Device Enrollment
  7. The Recovery Kit
  8. Local-first Sync
  9. Cloud Security Boundaries
  10. Conflicts, Deletion, and Failure
  11. Data Classification
  12. Account and Device Lifecycle
  13. Scope Boundaries
  14. Conclusion

1. The Product Boundary

Most AI products begin with a remote model endpoint. The client sends a prompt, a provider performs inference, and the provider returns a response. Tilde begins one boundary closer to the user: inference runs inside the native app, using a model stored on the device. The conversation can continue without a network connection, and the full transcript is not sent to a model provider as a condition of getting a response.

Local does not mean isolated. People replace phones, move between a phone and a computer, and expect their durable history to follow them. Tilde therefore separates two jobs that cloud AI products commonly combine:

  • Computation and comprehension happen locally, where plaintext is required.
  • Continuity and coordination happen through the cloud, where plaintext is not required.
TRUSTED DEVICE Platform secure storage vault key + device key seed Room database encrypted content + sync outbox On-device model plaintext exists only while the app uses it COORDINATION CLOUD Supabase Auth Email OTP + session app_api narrow RPC surface Internal relational data + change ledger ciphertext, envelopes, IDs, revisions, timestamps authenticated TLS opaque encrypted records OPTIONAL NETWORK TOOL Tilde Edge Function selected plaintext query External provider anonymous query only
Figure 1. Plaintext belongs to the trusted app process except when the user explicitly invokes a network tool. The selected query then passes transiently through Tilde’s Edge Function, which proxies it to the provider without Tilde account, device, or conversation identity.

The distinction is important: end-to-end encryption cannot hide text from a remote model that must read that text to perform inference. Tilde removes that routine disclosure by moving inference to the device. Encryption then protects durable content when it is stored or synchronized.

1.1 What is durable

A conversation contains ordered messages. A message contains ordered, self-describing events: user text, model output, reasoning state, tool calls, tool results, and other renderable content. Replaying those events reconstructs the message. Tilde also stores encrypted conversation titles and encrypted memories that help the local model provide continuity.

Model binaries, model installation state, drafts, transient generation state, hardware tuning, and ephemeral UI state remain device-local. Tilde synchronizes durable user state, not a live mirror of every process.

2. Design Principles

Local is canonical

The UI reads Room. A network request is never on the critical path of a local write.

Plaintext has a boundary

Content is decrypted where it is created or rendered, not inside sync or cloud storage.

Identity is not decryption

Email OTP authenticates an account. A trusted device or Recovery Kit unlocks its history.

One vault, many devices

A personal vault key encrypts content; each device receives it through its own envelope.

REST is enough for durable sync

Idempotent push and cursor pull provide durable-sync correctness; sockets belong to live escalation, not replication.

Recovery must be honest

If every trusted device and the Recovery Kit are lost, encrypted history cannot be recovered.

Deliberate simplicity

V1 is a single-owner personal vault. It does not pre-build multiplayer key distribution, live cross-device token streaming, key epochs, or a browser-client threat model.

3. Threat Model

Tilde’s cryptography is intended to keep durable content confidential when a database, backup, network trace, or cloud store is obtained without control of a trusted device. It is not intended to make a fully compromised device safe from itself.

Adversary or event Protected? Reason
Local database or backup extraction Yes Content values are authenticated ciphertext; keys live in platform secure storage.
Cloud database disclosure Yes The sync design stores opaque content and key envelopes, not the plaintext vault key.
Passive network observer Yes TLS protects transport; synchronized content is already encrypted end to end.
Compromised account email alone Yes Email OTP grants an authenticated session, not the vault key.
Stolen Recovery Kit alone Yes Recovery also requires an authenticated Email OTP session for the matching account.
Lost trusted device Partial Revocation stops future server access; Tilde cannot remotely erase data already downloaded.
Malicious code on an unlocked trusted device No The running app must access plaintext and the vault key to render and perform inference.
Account email plus Recovery Kit No This combination is intentionally sufficient to recover the synchronized history.
Traffic-shape or metadata analysis No Record IDs, revisions, timestamps, ciphertext sizes, and activity patterns remain visible.
Optional network-tool provider Partial The provider receives the selected query through Tilde’s proxy, but no Tilde account, device, or conversation identity.

3.1 Trusted components

  • The native Tilde app process while it is running.
  • Platform secure storage: Apple Keychain and Android Keystore-backed Tink storage.
  • The operating system sandbox and cryptographic random-number generator.
  • libsodium’s authenticated-encryption and sealed-box implementations.

3.2 Explicit non-goals

  • Protection after complete operating-system or app-process compromise.
  • Concealing that an account, device, conversation record, or synchronization event exists.
  • Remotely erasing an offline device or retroactively hiding content it already decrypted.
  • Cryptographic forward revocation in v1.
  • Hiding a deliberately submitted network-tool query from Tilde’s proxy or the selected provider.

4. Encrypted Local Foundation

Tilde’s implemented local store uses a single random 32-byte vault key and a versioned content format:

content_v1 =
  version(0x01)
  || nonce(24 bytes)
  || crypto_secretbox(plaintext, vault_key, nonce)

crypto_secretbox combines XSalsa20 encryption with a Poly1305 authenticator. A wrong key, modified ciphertext, or modified nonce produces an explicit authentication failure rather than plausible-looking garbage. The 24-byte random nonce makes accidental reuse negligible at Tilde’s write volume.

4.1 Key provisioning

On first use, the device generates a random Curve25519 key seed, derives a device keypair, generates the vault key, and seals a recoverable copy of the vault key to the device public key. The seed, cached vault key, public key, and sealed copy live in platform secure storage. The private key is derived only when needed and wiped from working memory afterward.

The current local implementation can recover a missing cached vault key from the sealed copy on the same device. The sync design strengthens this device identity by placing Tilde vault secrets in non-migrating, device-only secure storage so a backup restore does not silently clone a trusted device.

4.2 Encryption boundary

The repository layer encrypts content before persistence and decrypts it after reading. The sync layer handles the exact same bytes without opening them. At rest, opaque values use ByteArray/SQLite BLOB in Room and Postgres bytea in the cloud. Base64 is used only when binary data crosses a JSON RPC boundary.

Plaintext Application boundary

Message creation, rendering, local inference, and a deliberately invoked network tool.

Ciphertext Persistence boundary

Conversation titles, event payloads, memory summaries, and memory details.

Metadata Coordination boundary

Relationships, ordering, timestamps, record types, device status, and revisions.

5. The Personal Vault

A vault is the cryptographic and synchronization home for one account. V1 permits one active personal vault per account. Its 32-byte vault key encrypts all durable content, while an unrelated UUID identifies the vault in local and cloud data.

Devices do not share a private key or an account-wide seed. Each device creates its own independent 32-byte seed and Curve25519 keypair. The server stores the public key and a sealed envelope containing the vault key; it never receives the device seed, private key, or plaintext vault key.

Personal vault key random 32 bytes ENCRYPTS DURABLE CONTENT Encrypted durable content conversations · messages · events · memories Room is canonical · cloud stores ciphertext WRAPPED SEPARATELY FOR ACCESS Opaque vault-key envelopes Device A · Device B · Recovery Kit each receives its own independently wrapped copy
Figure 2. One key, two operations: encrypt durable content, and wrap the key separately for each access path. Adding a device adds an envelope without re-encrypting content.
Why not one account keypair?

Independent device keys prevent a backup or migration from silently cloning a trusted identity, keep revocation legible, and avoid making every device share one long-lived private root.

6. Trusted Device Enrollment

Email OTP answers “which account is requesting access?” It does not answer “may this device decrypt the vault?” Device enrollment is a separate ceremony:

1. Authenticate and request

The new device signs in by Email OTP, generates its device keypair, and registers the public key plus basic device metadata.

2. Wait for authority

The user chooses Approve from another device or I lost my recovery key. The app refreshes enrollment state at appropriate lifecycle moments and on explicit refresh; it does not poll continuously.

3. Approve locally

A trusted device obtains the pending public key, seals the vault key to it, and submits only that opaque envelope.

4. Open and verify

The new device downloads the envelope, opens it locally, and verifies the embedded purpose, vault ID, and its own device ID.

5. Bootstrap history

Only after vault access and any local/cloud merge decision are resolved does the device download encrypted history.

The approving and requesting devices need no direct connection. Supabase is the mailbox for the sealed envelope and records which authenticated trusted device authorized it. The ceremony relies on authenticated backend integrity and does not use QR binding, number matching, key transparency, or biometric step-up.

Device enrollment does not rely on push notifications. A pending request appears when the trusted device next refreshes its enrollment state. A request cancelled from another device remains as a terminal revoked row long enough for the waiting client to show a clear cancellation state instead of becoming silently orphaned.

7. The Recovery Kit

A Recovery Kit is the fallback authority when no trusted device is available. It combines a uniformly random 160-bit Recovery Key with a recovery envelope stored by Supabase. Recovery requires both:

  1. a valid Email OTP session for the account; and
  2. the Recovery Key that opens the current recovery envelope.

Neither factor is sufficient alone. The server can return the opaque envelope to an authenticated pending device, but only the Recovery Key can reveal the vault key and a random proof token inside it. The client returns that proof over authenticated TLS to demonstrate that it opened the envelope before the server trusts the device.

7.1 Human format

TILDE-RK1-XXXX-XXXX-XXXX-XXXX-XXXX-XXXX-XXXX-XXXX-CCCC

The eight secret groups encode 20 random bytes using Crockford Base32. The final group is a 20-bit checksum that catches common copy and transcription errors; it does not add authorization. Tilde deliberately uses a random string rather than a mnemonic phrase or user-selected PIN.

Setup creates a one-page PDF with the account email at creation, creation date, QR code, grouped Recovery Key, and short instructions. It also offers a recovery URL:

https://tildeapp.ai/recover#<grouped-recovery-key>

The secret is placed after the URL fragment marker, so a conforming browser does not send it to the web server. The URL contains no email, account ID, vault ID, or device ID. Tilde never emails or uploads the PDF automatically; the user deliberately exports it or stores the URL.

A mandatory checkbox acknowledges that the kit was saved before initial vault creation completes. Tilde does not pretend it can verify the destination. The plaintext key is shown only during creation and is not retained for a later “view kit” action.

7.2 Recovery does not mean incident response

Routine recovery on a replacement phone adds that phone as a trusted device without revoking a Mac or older phone. After recovery, Tilde offers to keep the current kit or create a replacement. The Trusted Devices screen also provides an explicit Replace Recovery Kit action and a separate Sign Out All Other Devices action.

Replacing a kit is atomic: the old kit remains valid until the user acknowledges saving the new PDF or URL and the replacement transaction commits. Tilde does not attempt to verify where the kit was saved. A crash before commit leaves the old kit usable.

7.3 When recovery is impossible

If every trusted device and the Recovery Kit are lost, the vault key is unavailable by design. The user may still authenticate by email and choose Reset Encrypted History & Start Fresh. This atomically purges the old encrypted vault, revokes its devices and recovery material, and creates a new unrelated vault, vault key, and Recovery Kit. Account identity and non-secret account state survive; encrypted history does not.

8. Local-first Sync

Sync extends the encrypted local store rather than replacing it. Room remains the UI source of truth on iOS, Android, and macOS. A local edit updates the domain row and its outbox entry in one database transaction, so a crash cannot preserve one without the other.

DEVICE · ROOM Local write transaction encrypted row + outbox mutation Encrypted local state the UI updates immediately sync_state cursor SUPABASE · POSTGRES sync_push small all-or-nothing mutation batch Relational rows + change ledger consecutive per-vault revisions sync_pull(after_revision) ciphertext mutations ordered changes
Figure 3. Both critical pairs are atomic: local row + outbox on write, and downloaded records + cursor on pull.

8.1 Three operations

RPC Purpose Client state
sync_bootstrap Page through the complete encrypted vault for a new or reset device. Opaque page token; final baseline revision.
sync_pull Read ordered upserts and deletes after the local revision cursor. last_applied_revision.
sync_push Commit a bounded, dependency-safe batch of idempotent local mutations. Stable mutation UUIDs in the Room outbox.

8.2 Bootstrap without a moving target

The first bootstrap page captures the current vault ledger revision as a baseline. Every subsequent page uses a signed, opaque token bound to that baseline and the authenticated device. The server does not hold a database snapshot open across requests. Writes committed after the baseline receive later revisions; once the final page is applied, an ordinary pull catches up from the baseline and closes the race.

8.3 Incremental pull

Pull accepts only after_revision. The server derives the account, trusted device, and vault from the authenticated session and controls the page size. Changes arrive in ascending revision order. Room applies the page and advances its cursor to the final revision in the same transaction.

8.4 Atomic, idempotent push

Every mutation has a UUID. The server locks the vault revision counter, recognizes already committed UUIDs as successful retries, validates new records and relationships, applies them, allocates consecutive revisions, and inserts matching ledger entries in one transaction. Any invalid mutation rolls back the whole chunk. The client then removes only the exact outbox generations acknowledged by the response.

Large backlogs drain through bounded, dependency-safe chunks. A conversation may span chunks as long as parents are already committed or precede their children. Server configuration controls decoded byte and mutation-count limits.

8.5 When sync runs

Sync runs opportunistically as the app and local data change, and users can request it explicitly. Transient errors use bounded retry with backoff and leave the durable outbox intact for a later attempt. There is no endless foreground polling.

Completed messages synchronize; token-by-token generation does not. Realtime, silent push, and specialized replication engines sit outside the authoritative sync path; REST batches provide correctness.

9. Cloud Security Boundaries

Tilde uses Supabase Auth for Email OTP sessions and Postgres for coordination and encrypted storage. Native clients contain only a publishable credential. They receive no service-role key and no direct CRUD privileges on application tables.

AUTHENTICATED APP DATA PATH Native apps user JWT app_api granted app RPCs internal_api auth + implementation Encrypted sync data internal_data · forced RLS vaults · devices · content · ledger ATTESTED EDGE FUNCTION PATH Native apps user JWT App Attest assertion Attested Edge Function session + assertion validation request validation · rate limits service_api allowlisted RPCs only Private attestation state internal_data · forced RLS keys · challenges · rate limits
Figure 4. Native apps use app_api for encrypted continuity and invoke attested Edge Functions for optional network tools. Edge Functions can reach Postgres only through the narrow service_api; neither path directly accesses internal tables.

9.1 Four Postgres schemas

Schema Visible to Contains
app_api Authenticated native apps through PostgREST Thin, intentionally granted app RPC entrypoints; no tables.
service_api Tilde Edge Functions through a module-private service-role client Allowlisted App Attest and rate-limit RPCs; no tables or vault-content access.
internal_api Database implementation only Shared authorization, ledger, mutation, and privileged helper functions.
internal_data Database implementation only Vault, device, content, ledger, and private Edge Function state.

9.2 Defense in depth

Direct SELECT, INSERT, UPDATE, and DELETE privileges are revoked from anonymous and authenticated clients. Every application table enables and forces row-level security as a second isolation layer. Each RPC still performs explicit account, live-session, trusted-device, and vault checks; RLS is not treated as a substitute for ceremony-specific authorization.

Privileged database functions use an empty search path, fully qualified objects, and narrowly granted ownership roles without BYPASSRLS. The service-role credential is confined to service_api: it has no direct internal_data privileges or table grants. Each allowlisted RPC runs through a no-login, no-BYPASSRLS executor with only the internal table privileges that operation requires.

9.3 Edge Functions

Edge Function source lives under supabase/functions, not inside a Postgres schema. Tilde’s web-tool handlers validate the caller’s Supabase session inside the function, verify App Attest, rate-limit the request, and validate its input. They do not forward the caller’s JWT to app_api. For attestation keys, one-time challenges, replay counters, and rate-limit windows, the handlers use a module-private service-role client to call an allowlisted service_api RPCs. Those RPCs have no path to vault, conversation, message, event, memory, or sync-ledger tables.

Optional web search, current-information, and places tools are a separate privacy boundary. When enabled and invoked, the selected plaintext query and provider response pass transiently through Tilde’s attested Edge Function. The function makes the provider request with Tilde’s credentials and does not forward the user’s email, account ID, device ID, JWT, conversation ID, unrelated conversation history, or vault key. The external provider receives the query text needed to answer the request, but receives it as an anonymous query from Tilde’s proxy.

10. Conflicts, Deletion, and Failure

10.1 Conversation divergence

Every message records the previous message known when it was created. If two offline devices add different children to the same predecessor, the server rejects the later atomic batch rather than interleaving branches. The client pulls the committed branch and moves only its unsynchronized tail into a new conversation whose title carries a localized Split marker. Both pieces of work survive without introducing a full branching-history model.

10.2 Metadata conflicts

Renames, pinning, and other ordinary metadata use deterministic server-commit-order, whole-record last-write-wins behavior. Device clocks are not authoritative. last_activity_at is updated in the same local transaction as a message commit. Like other whole-record conversation metadata, conflicting cloud writes resolve in server commit order.

10.3 Deletion wins

A conversation deletion first enters a short local pending state and presents an Undo toast. Only after that window expires does the client synchronize a tombstone. Postgres and Room cascade messages and events; memory provenance becomes null; encrypted payloads are purged. A stale offline upsert cannot resurrect the deleted record.

10.4 Permanent rejection

A structurally invalid mutation is treated as an application or schema defect, not a routine user conflict. The client follows dependencies from the bad record to its conversation, excludes that conversation’s complete pending tail for the rest of the process session, and continues syncing unrelated data. A cold launch gives it one fresh attempt.

V1 records a privacy-safe PostHog event with operational categories and batch-shape metadata. It sends no IDs, titles, content, ciphertext, keys, envelopes, tokens, or payloads. Persistent quarantine UI and diagnostic submission are outside this architecture.

11. Data Classification

Data Local storage Cloud storage Server-visible?
Conversation titles Encrypted BLOB Encrypted bytea No plaintext
Message event payloads Encrypted BLOB Encrypted bytea No plaintext
Memory summary and detail Encrypted BLOB Encrypted bytea No plaintext
Vault and record IDs Plain UUID Plain UUID Yes
Relationships and event positions Plain metadata Plain metadata Yes
Timestamps, revisions, ciphertext sizes Plain metadata Plain metadata Yes
Device platform, model, app/OS version, last seen Plain metadata Plain metadata Yes
Device private key seed and cached vault key Device-only secure storage Never stored No
Device and recovery envelopes Opaque bytes as needed Opaque bytea Ciphertext only
Recovery Key Shown only during creation/use Never stored No
Email address and Auth session Session in secure storage Supabase Auth Yes, to Auth
Optional web-tool query Available to the app Not stored; transiently proxied by Tilde Plaintext to Tilde; anonymous query to provider

Tilde does not claim metadata invisibility. Supabase can infer approximate activity timing, device population, record counts, and encrypted payload sizes. V1 does not use padding, cover traffic, or private-information-retrieval techniques.

12. Account and Device Lifecycle

12.1 Sign in when local data already exists

A device may contain unaffiliated local conversations before the user signs into an account whose vault already has history. Tilde waits until the device has cryptographically opened an approval or recovery envelope, then asks the user to Merge & Continue, Discard Local Data & Continue, or Back Out of Sign In.

It does not download the account history before this decision. The pending choice is stored globally and bound to the target vault so a crash or interrupted login cannot mix data between accounts. A merge bootstraps the account store before importing and re-encrypting local content; a discard verifies the account bootstrap before deleting the local source.

12.2 Sign out

Sign out revokes only the current Tilde device registration, clears its local Supabase session, and deletes its account database, device key, and vault key. It does not revoke other trusted devices. If it is the last trusted device, Tilde warns that returning will require the Recovery Kit or a destructive start fresh.

12.3 Remove a device

A trusted device may revoke another trusted or pending device. Every protected RPC checks the live Tilde device registration in addition to the Supabase JWT, so revocation takes effect even while an old access token remains cryptographically valid. The removed app signs out with an explanation on its next server contact.

13. Scope Boundaries

  • Single-owner vault. Every trusted device has the same owner authority. The vault does not support shared conversations, participant key distribution, or member-level access.
  • Remote-agent access. Tilde does not disclose the personal vault key or continuous conversation context to remote agents. External tool providers receive only deliberately selected, anonymous queries through Tilde’s proxy.
  • Authentication and recovery. Email OTP authenticates the account, and the Recovery Key provides last-resort vault access. Passkeys do not participate in this protocol.
  • Revocation. Device removal denies future Tilde server access but does not rotate content keys or remotely erase content already downloaded by that device.
  • Device approval. Approval uses the authenticated backend mailbox and device-bound sealed envelopes. It does not include number matching, QR binding, key transparency, or system-authentication step-up.
  • Sync delivery. REST is authoritative. Push and realtime delivery are not part of the correctness model.
  • Client trust. The protocol covers installed trusted-device clients. Browser and temporary-computer access are outside its trust model.
  • Metadata exposure. Records do not use padding, cover traffic, or access-pattern defenses.
  • Ledger retention. The server retains ledger metadata indefinitely and does not compact the ordered history.

14. Conclusion

Tilde’s privacy model is a division of responsibility. The device is trusted with understanding: it runs the model, holds the keys, and renders plaintext. The cloud is trusted with continuity: it authenticates accounts, coordinates devices, and stores opaque records in commit order. The Recovery Kit is trusted with last-resort access, but only alongside the account email.

No single mechanism carries the whole claim. On-device inference removes the routine model provider. Authenticated encryption protects durable content. Independent device keys make access explicit. A narrow REST/RPC layer and forced RLS reduce the cloud attack surface. Honest recovery language acknowledges the unavoidable trade: if no trusted device and no Recovery Kit can reveal the vault key, Tilde cannot reveal the history either.

Local where intelligence happens. Encrypted where continuity happens.


Appendix A: Wire Formats

All multibyte identifiers use their canonical 16-byte UUID representation. Opaque binary values are stored as BLOB/bytea and Base64 encoded only inside JSON transport.

A.1 ContentCiphertextV1

format_version     1 byte   0x01
nonce             24 bytes random
auth_tag          16 bytes Poly1305
encrypted_message  n bytes XSalsa20 output

The encoded value is version || nonce || auth_tag || encrypted_message, matching libsodium’s combined crypto_secretbox_easy output.

A.2 DeviceVaultKeyEnvelopeV1

outer:
  format_version             1 byte   0x01
  sealed_box               113 bytes  crypto_box_seal(authenticated_plaintext)

authenticated_plaintext:
  purpose                    1 byte   DEVICE_VAULT_KEY (0x01)
  vault_id                  16 bytes
  recipient_device_id       16 bytes
  vault_key                 32 bytes

total                      114 bytes

The server validates only the outer version and fixed length. The recipient opens the envelope and verifies its sealed purpose, vault ID, and device ID.

A.3 Recovery Key

secret         20 random bytes (160 bits)
encoding       Crockford Base32, 32 characters
checksum       first 20 bits of BLAKE2b-256(
                 "TILDE-RK1-CHECKSUM" || secret
               )
display        TILDE-RK1-[8 x 4 secret chars]-[4 checksum chars]

Input parsing is case-insensitive, ignores whitespace and hyphens, and maps commonly confused characters O→0 and I/L→1. Output always uses canonical uppercase grouping.

A.4 RecoveryVaultKeyEnvelopeV1

recovery_wrapping_key =
  BLAKE2b(
    output_length = 32,
    key = raw_recovery_secret,
    message = "TILDE-RK-WRAP-V1" || vault_id
  )

outer:
  format_version             1 byte   0x01
  nonce                     24 bytes
  secretbox                 97 bytes

authenticated_plaintext:
  purpose                    1 byte   RECOVERY_VAULT_KEY (0x02)
  vault_id                  16 bytes
  vault_key                 32 bytes
  proof_token               32 bytes random

total                      122 bytes

Supabase stores SHA-256(proof_token) alongside the envelope. The proof token itself remains encrypted until a correct Recovery Key opens the envelope.

Appendix B: Cloud Information Model

Personal vault ownership · recovery · ordering Trusted devices status · key delivery Conversations encrypted title · activity Memories encrypted summary + detail Change history ordered revisions Messages role · previous message Events ordered encrypted payloads optional source message
Figure 5. The cloud preserves only the relationships and ordering needed to reconstruct the encrypted local history.

Cloud records contain opaque identifiers, encrypted payloads and key envelopes, ordering metadata, and timestamps. Room remains the canonical working copy and separately tracks unsynchronized local changes and the last applied cloud revision.

Appendix C: Cloud API Boundaries

Capability Permitted action Required authority
Enrollment Create, inspect, or cancel a pending device enrollment and initialize a personal vault. Authenticated pending session with ceremony-specific limits.
Device management List, approve, revoke, or sign out devices associated with the vault. Current trusted device; self-sign-out is a distinct path.
Recovery Retrieve opaque recovery material, complete recovery, replace a kit, or start fresh. Pending session plus proof for recovery; trusted device for replacement; explicit data-loss acknowledgment for reset.
Synchronization Bootstrap encrypted history, pull later revisions, or push an atomic mutation batch. Authenticated, currently trusted device only.

Requests omit account, vault, caller-device, and auth-session identifiers whenever the server can derive them. Client-provided UUIDs identify target records; they are never treated as authorization capabilities.

Appendix D: Glossary and References

D.1 Glossary

Envelope
An opaque encrypted artifact that delivers the vault key to one device or Recovery Key.
Ledger revision
A monotonically increasing, per-vault commit position used for incremental synchronization.
Personal vault
The encrypted content and key-distribution domain owned by one Tilde account.
Recovery proof
A random token encrypted inside the recovery envelope and hashed by the server, proving that a client opened the current envelope.
Trusted device
A device whose registration is active and that can open its own envelope to obtain the vault key.

D.2 References

  1. libsodium: Authenticated encryption with secret-key cryptography
  2. libsodium: Sealed boxes
  3. libsodium: Generic hashing
  4. PostgreSQL: Row security policies
  5. Supabase: Row Level Security