Arc Forge Labs Docs v2026.10.2

Dictate Cloud Sync And Account Plan

Date: 2026-07-05

Status: Reference and migration input, subordinate to docs/goal.md.

Superseded claims: Hosted results use server-managed encrypted artifacts (AES+AAD account isolation); owner-bound device-key wrapping is deferred per goal.md Wave 2. Process-memory auth and dual commerce reads are interim only — not intentional production design.

This document supplies scoped detail for Dictate's use of the Arc Forge account, encrypted cloud sync, and Dictate Pro paid capabilities. docs/goal.md is the current cross-system goal and authority for identity, product/account boundaries, commerce, hosted work, and release decisions. Dictate remains local-first: the app must work without an account, and signing out must not delete local dictations.

Goal

Ship Dictate's Arc Forge account integration and Dictate Pro capabilities without per-customer VPS infrastructure:

  1. Use the existing Arc Forge account and gateway path for identity, entitlements, subscriptions, usage, and hosted model access.
  2. Add a central multi-tenant sync service for Dictate data.
  3. Encrypt dictated content so server operators cannot read synced dictations.
  4. Keep everyday local dictation private by default and make cloud sync a clear opt-in.
  5. Sync useful account preferences across devices while keeping device-specific hardware/runtime settings local.

Privacy Contract

Customer-facing promise:

Dictate can run locally without cloud sync. If you enable Dictate Pro sync, your dictated text is encrypted before it leaves your device. Arc Forge stores encrypted records needed to sync your devices, but Arc Forge staff cannot read your dictated content.

Non-negotiable rules:

  1. Dictation text, note segments, meeting transcripts, and synced lexicon terms are encrypted client-side before upload.
  2. Server logs, analytics, billing records, and support tooling must never contain plaintext dictated content.
  3. Raw audio is not synced by default.
  4. API keys, refresh tokens, and provider credentials are never synced.
  5. A user can export local data and request deletion of cloud data.
  6. Account sign-in and hosted Pro transcription are separate from sync consent: signing in must not silently upload local history.

Existing Repo Context

Current local data is already friendly to sync:

Area Current shape Sync implication
Recent history recent-history.json with id, created_at, text, archived Convert to per-record changes; local MAX_ENTRIES truncation must not delete server records.
Long notes notes/<note_id>/note.json plus append-only segments.jsonl Sync note metadata and immutable segment records separately.
Pro client src/dictate/pro/client.py uses https://console.arcforge.au by default Reuse this gateway session for sync auth.
Pro control plane src/dictate/pro/service.py models accounts, entitlements, usage, and meeting jobs Keep as dev/test harness; production should use Arc Forge gateway.
Refresh tokens Stored through the OS secret store where available Keep this pattern; do not put account secrets in synced records.

Architecture

Use one central multi-tenant Arc Forge deployment with three logical concerns:

Component Role
Arc Forge account/gateway Login, device registration, subscription state, entitlements, usage metering, hosted model jobs.
Dictate sync API Record push/pull, encrypted payload storage, cursors, conflict handling, deletion.
Postgres Multi-tenant authoritative sync store keyed by account_id.
Object storage Optional large encrypted blobs later; not used for normal note/history sync.
Dictate desktop app Local-first store, client-side encryption, outbox journal, background sync loop.

The sync API can start inside the Arc Forge gateway because the gateway already owns auth, rate limiting, billing, and entitlement checks. Split it into a separate service only if deployment pressure proves it necessary.

Identity And Entitlements

Account concepts:

Identifier Meaning
account_id Billing and data ownership boundary.
user_id Human identity inside an account; optional for single-user launch.
device_id Stable per-install UUID generated by Dictate and registered at sign-in.
session_id Short-lived auth/session context.

Required gateway additions:

  1. sync entitlement on Dictate Pro plans.
  2. Device registration endpoint that accepts a device label and client public keys.
  3. Device revocation endpoint.
  4. Account deletion/export jobs.
  5. Sync usage/storage counters, separate from hosted transcription usage.

Initial account model can be single-user-per-account. The data model should still include account_id everywhere so team/shared accounts can be added without a storage rewrite.

Encryption Model

Use end-to-end encrypted payloads for synced dictation content.

Keys

Key Location Purpose
Account Data Key, ADK Generated on first sync-enable device; never uploaded plaintext Encrypts record payload keys or directly encrypts small payloads.
Device Key Pair Generated per device; private key in OS secret store Wraps/unlocks the ADK for that device.
Recovery Key Shown once or saved by user; optional in MVP but strongly recommended before broad release Restores account data if all devices are lost.
Server Signing Key Server side Signs device-registration and key-envelope metadata.

First Device Flow

  1. User signs into Arc Forge for Dictate.
  2. User explicitly enables cloud sync.
  3. App generates device_id, device key pair, and ADK.
  4. App stores private keys in OS secret store.
  5. App uploads public device metadata and an encrypted account-key envelope.
  6. App starts syncing encrypted records.

Additional Device Flow

Preferred secure flow:

  1. New device signs into the same Arc Forge account.
  2. Server marks it as pending and shows a short pairing code.
  3. Existing trusted device approves the new device.
  4. Existing device wraps the ADK to the new device public key.
  5. New device decrypts the key envelope locally and starts sync.

Fallback recovery flow:

  1. User enters recovery key on the new device.
  2. App derives/unlocks the ADK.
  3. Server marks the device trusted.

Do not let the server issue itself an account data key. If server-side recovery is ever offered, label it as less private because Arc Forge would be able to recover content.

Payload Encryption

Use authenticated encryption for every synced payload:

ciphertext = XChaCha20-Poly1305(
  key = ADK or per-record DEK,
  nonce = random 192-bit nonce,
  aad = account_id | collection | record_id | rev | device_id
)

AES-256-GCM is also acceptable if the platform crypto library makes it easier, but nonce management must be strict. XChaCha20-Poly1305 is safer for random nonces in a desktop app.

Server-visible metadata should be minimal:

Server can see Server cannot see
Account id, device id, collection, record id, revision, timestamps, payload size, tombstone flag Dictated text, transcript segments, note titles if derived from text, hotwords, lexicon terms, meeting transcript content.

If a note title is generated from dictated text, encrypt it as content. Use a generic local-only display fallback until decrypted.

Sync Data Model

Postgres tables:

devices (
  account_id text not null,
  device_id text not null,
  label text not null,
  public_key text not null,
  trusted_at timestamptz,
  revoked_at timestamptz,
  last_seen_at timestamptz,
  primary key (account_id, device_id)
);

key_envelopes (
  account_id text not null,
  device_id text not null,
  envelope_version integer not null,
  encrypted_adk bytea not null,
  created_at timestamptz not null,
  primary key (account_id, device_id, envelope_version)
);

sync_records (
  account_id text not null,
  collection text not null,
  record_id text not null,
  seq bigserial not null,
  rev integer not null,
  device_id text not null,
  updated_at timestamptz not null,
  deleted boolean not null default false,
  content_type text not null,
  ciphertext bytea not null,
  nonce bytea not null,
  aad_hash bytea not null,
  payload_bytes integer not null,
  primary key (account_id, collection, record_id)
);

sync_cursors (
  account_id text not null,
  device_id text not null,
  last_seq bigint not null default 0,
  updated_at timestamptz not null,
  primary key (account_id, device_id)
);

Required indexes:

create index sync_records_account_seq on sync_records(account_id, seq);
create index sync_records_account_device on sync_records(account_id, device_id);

Collections:

Collection Record id Payload
history history entry id Text, created time, archived state.
note note id Mode, provider/model labels, status, timestamps, archived state.
segment note_id:seq Segment text, timestamps, speaker labels.
settings setting key Synced preference value.
lexicon normalized term id Hotwords, substitutions, custom vocabulary.

Sync Protocol

Push

POST /api/dictate/sync/push
Authorization: Bearer <access_token>

Request:

{
  "device_id": "device_...",
  "records": [
    {
      "collection": "segment",
      "record_id": "note_abc:12",
      "rev": 1,
      "updated_at": "2026-07-05T12:00:00Z",
      "deleted": false,
      "content_type": "application/vnd.dictate.segment+json;v=1",
      "ciphertext": "...base64...",
      "nonce": "...base64...",
      "aad_hash": "...base64..."
    }
  ]
}

Server behavior:

  1. Validate token, account, device, entitlement, payload size, and rate limits.
  2. Reject revoked or untrusted devices.
  3. Upsert only if incoming (rev, updated_at, device_id) wins over stored metadata.
  4. Return accepted, rejected, and current-winning records.
  5. Never decrypt payloads.

Pull

GET /api/dictate/sync/changes?since=<seq>&limit=500
Authorization: Bearer <access_token>

Response:

{
  "next_seq": 12345,
  "has_more": false,
  "records": []
}

Client behavior:

  1. Push outbox first.
  2. Pull changes after the last stored cursor.
  3. Verify metadata and decrypt locally.
  4. Apply accepted records to the local store.
  5. Advance the cursor only after local apply succeeds.

Conflict Rules

Dictate data should remain mostly conflict-free.

Data Rule
History entries Immutable create by unique id; merge by union.
Note records One originating device; mutable fields use last-writer-wins.
Segments Immutable create by note_id:seq; only originating device appends.
Archive/unarchive Last-writer-wins by logical revision then timestamp.
Settings Field-level last-writer-wins.
Deletes Tombstones sync to all devices, then server garbage-collects after retention.

The local recent-history size limit must never be treated as a delete. The cloud store is the durable account history; each device may show a local window or a cloud-backed full list.

What Syncs

Sync by default once the user opts into encrypted sync:

  • Dictation history text.
  • Long-form note metadata.
  • Transcript segments.
  • Meeting transcript and speaker/timestamp metadata.
  • Archive state.
  • Hotwords, custom vocabulary, spelling substitutions, and output formatting preferences.
  • Product preferences that are not hardware-specific.

Keep local per device:

  • Microphone and output-device choices.
  • CPU/GPU/runtime lane.
  • Local model cache paths.
  • API keys and provider tokens.
  • Refresh tokens.
  • OS-specific hotkeys unless explicitly marked portable.
  • Raw audio.

Optional later sync:

  • Encrypted raw audio blobs for users who explicitly enable cloud audio backup.
  • Encrypted attachments/imported meeting files.
  • Team/shared workspace records.

Hosted Models And Privacy Boundary

Hosted Pro transcription is different from sync:

  1. User intentionally sends audio to a hosted model job.
  2. The gateway meters usage and talks to the provider.
  3. Returned transcripts are sent back to the requesting client, but the Dictate control plane must not persist readable transcript text.
  4. Returned transcripts are encrypted by the client before entering sync.
  5. Provider-side retention and privacy terms must be disclosed separately from Dictate sync storage.

For the strongest privacy posture, prefer local transcription for normal dictation and reserve hosted models for explicit Pro workflows where users understand audio leaves the device.

UI Requirements

Product language should be simple:

  • Sign in to Arc Forge for Dictate
  • Sync my dictations across devices
  • This encrypts your synced dictations before upload
  • Approve this device
  • Export my data
  • Delete cloud data

Do not expose implementation terms such as E2EE, ADK, cursor, or sync_records in the primary UI. They can appear in advanced diagnostics.

Minimum UI surfaces:

  1. Account panel hidden behind the Dictate icon/settings area.
  2. Sync opt-in toggle with clear privacy copy.
  3. Device list with revoke action.
  4. Sync status: Up to date, Offline, Syncing, Action needed.
  5. Recovery key setup and warning.
  6. Export/delete cloud data actions.

Advanced CLI parity is required for support, packaged-app recovery, and headless testing:

Command Purpose
dictate pro status Show sign-in, entitlement, and encrypted-sync state.
dictate pro sign-in <email> / dictate pro verify ... Start and complete Arc Forge sign-in for Dictate while registering this device's sync public key.
dictate pro sync enable [--recovery-key ...] Explicitly opt into encrypted sync or restore an existing sync key.
dictate pro sync run Push local encrypted outbox records and pull remote changes once.
dictate pro sync disable [--clear-key] Disable sync on this device without deleting local dictations.
dictate pro devices list|approve|revoke Review, trust, and revoke devices.
dictate pro cloud export / dictate pro cloud delete --yes Export or delete account-owned cloud records.

Threat Model

Protect against:

  1. Server database compromise.
  2. Curious or mistaken server operator access.
  3. Network interception.
  4. Lost or revoked devices.
  5. Accidental logging of private content.
  6. Cross-tenant data exposure.

Accepted MVP limitations:

  1. Server still sees metadata such as account id, device id, sync volume, record counts, and update timing.
  2. If a user loses all trusted devices and no recovery key exists, encrypted synced content is unrecoverable.
  3. Hosted transcription providers may see audio/transcript content for jobs the user explicitly sends to them.

Controls:

  • TLS everywhere.
  • Row-level account scoping and authorization checks.
  • Client-side encryption for payloads.
  • Private keys in OS secret store.
  • No plaintext payload logging.
  • Strict request size limits.
  • Device revocation.
  • Account deletion purge.
  • Periodic security review before stable release.

Implementation Phases

Phase 0: Local Foundations

  • Generate and persist stable install device_id.
  • Split settings into synced and device_local namespaces.
  • Add local revision/update metadata to history, notes, and settings.
  • Add an outbox journal for local mutations.
  • Add tests proving local history truncation does not create delete events.

Phase 1: Crypto Foundation

  • Add local key generation and storage.
  • Add payload encrypt/decrypt helpers.
  • Add encrypted record envelope format.
  • Add recovery-key creation and verification flow.
  • Add tests with known vectors and wrong-key failures.

Phase 2: Gateway Sync API

  • Add Dictate Pro sync entitlement.
  • Add device registration and revocation.
  • Add devices, key_envelopes, sync_records, and sync_cursors.
  • Add push/pull endpoints.
  • Enforce account scoping, rate limits, payload caps, and no plaintext logging.

Phase 3: Client Sync Engine

  • Sign in through existing Pro client.
  • Enable sync only after explicit user consent.
  • Push encrypted outbox records.
  • Pull and decrypt remote changes.
  • Merge history, notes, segments, settings, and lexicon records.
  • Surface quiet sync status in the app.

Phase 4: Multi-Device And Recovery

  • Add approve-new-device flow.
  • Add recovery-key restore flow.
  • Add device list and revoke action.
  • Add first-sync merge tests for two populated devices.
  • Add account export/delete flows.

Phase 5: Hardening And Stable Promotion

  • Load-test push/pull with realistic note volumes.
  • Add cloud sync integration tests in CI.
  • Add Windows and Linux packaged-app sync smoke tests.
  • Run privacy review for logs, crash reports, support bundles, and analytics.
  • Update privacy policy, terms, Store listing, and Pro onboarding copy.

Acceptance Gates

This plan is complete when:

  1. A user can sign into Arc Forge for Dictate on two devices and opt into Dictate Pro sync.
  2. Dictations created offline on one device sync when network returns.
  3. Synced ciphertext in Postgres cannot be read without the user's account data key.
  4. Revoking a device stops future sync from that device.
  5. The server can delete all cloud records for an account.
  6. Local data survives sign-out.
  7. Tests prove no plaintext dictated content is logged or stored server-side.
  8. Hosted model usage remains governed by Arc Forge entitlement and usage limits.

Explicit Non-Goals

  • Per-customer VPS deployments.
  • Google Drive, Dropbox, or OneDrive as the primary sync engine.
  • Server-side full-text search over dictated content.
  • Silent upload on sign-in.
  • Raw audio sync by default.
  • Server-managed recovery that allows Arc Forge staff to decrypt user content.