Skip to content

id: investigate: hosted data option — per-user stores, Google SSO, static/local-storage variants, container-spend limits #2045

Closed

Description

@dezren39

LIST 12b. The hosted offer: maybe a SQLite per user who signs in with Google SSO — 'our way of having a cloud-hosted version that they can host their data in'. Investigate: per-user store provisioning and isolation, SSO→ID binding (how a Google identity relates to an Iroh ID), static sites using local storage and websites-with-backends as alternative shapes, and the cost guardrail — a little container use is acceptable, indefinite per-user-interaction container spend is not. Deliverable: hosted-shape comparison with rough per-user cost drivers.

Program: Drew's 2026-10-28 directive (full text: hatch-personal memory/topics/id-program-2026-10-28.md).
Working pattern: parallel investigations, then the named synthesis issue merges them into a full design. Route guidance: Workhorse by default; Specialist/Summit for synthesis and ramification-heavy work. A result that says "done" is not the endpoint — the orchestrator reviews it and may commission another round. Deliverables: a markdown design/investigation doc (mermaid/SVG diagrams welcome) linked from this issue, plus follow-up issues for anything buildable. Build a proof of concept where the design calls for one, but do NOT build the permanent feature on top of an unreviewed PoC.

Activity

  1. added
    app/idPackage/app: id (pkgs/ or apps/ in this repo)
    on Oct 29, 2026
  2. developing-today commented on Oct 29, 2026

    @developing-today
    Contributor
  3. dezren39 commented on Oct 29, 2026

    @dezren39
    MemberAuthor

    Parent epic: #2056 — ID platform expansion (2026-10-28 directive).

  4. added
    priority/highImportant and time-sensitive
    queue/queuedHas a queued task in the Supabase agent queue
    on Oct 29, 2026
  5. dezren39 commented on Oct 29, 2026

    @dezren39
    MemberAuthor

    Top priority — ID program wave-1. Drew directed this be picked up in the next runs (2026-10-29): enqueued directly as triage task 3354 in repo-health-code-pilot at queue priority 20 (only breakage-class work at priority 10 outranks it). Wave-1 is the ungated investigations and designs; syntheses and PoCs stay gated on their named inputs. Investigation route: Workhorse.

  6. added
    work/assignedQueue worker lifecycle state
    queue/claimedQueue worker lifecycle state
    and removed
    queue/queuedHas a queued task in the Supabase agent queue
    on Oct 29, 2026
  7. dezren39 commented on Oct 29, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · picked up

  8. dezren39 commented on Oct 29, 2026

    @dezren39
    MemberAuthor

    Investigation result — hosted data option (#2045, queue task 3354)

    Verdict

    The hosted offer is viable in exactly the shape Drew sketched — per-user store, Google SSO sign-in, no per-user containers — and the #2044 Workers-fit investigation already settled the platform half: one Durable Object per user (SQLite-backed, strongly consistent, serialized per user, ~1,000 req/s soft cap per object, 10 GB per object on Paid) is the per-user server + database, costs nothing while idle, and the hosted edition is necessarily an HTTP/WebSocket-shaped subset because Workers have no UDP for Iroh transport. This investigation covers what #2044 did not: provisioning and isolation, the SSO→ID binding (the subtle part), the alternative shapes, and the cost guardrails.

    Provisioning and isolation

    • Lazy provisioning: first Google sign-in creates the account record and names the user's Durable Object (idFromName over the binding ID); the DO and its SQLite schema materialize on first use. No sign-up-time fleet of anything; an account that never returns costs one small account row.
    • Isolation is structural, not query discipline: one user's objects live in one DO's storage; there is no shared table in which a missing WHERE user = ? leaks across users. Cross-user interaction never touches another user's store directly — it moves signed graph objects through the platform's normal exchange surfaces (shared worlds, messages, public stores), where the receiving store applies its own ingest rules (id: investigate: trust/visibility model — servers accept all signed objects; views count only allowed writers #2030: validity at ingest, counting at view). A hosted user and a self-hosted peer are indistinguishable at the object layer.
    • Blobs: large content goes to R2 (content-addressed, deduplicated across users — identical blobs stored once), with per-user reference rows in their SQLite. DO SQLite rows cap at 2 MB (id: investigate: Cloudflare Workers edition — isolates + Durable Objects + per-user SQLite fit for ID #2044), so this split is forced anyway; dedupe is safe because blobs are addressed by hash and private content is client-encrypted before upload (below), making stored blobs opaque.
    • Tenant ceilings ride along: the per-DO caps (10 GB, ~1,000 req/s) are per-user blast-radius limits for free — one abusive or runaway user degrades their own object, not the fleet (fleet-level abuse controls remain id: investigate: comms abuse controls — blocks, rate limits, and per-counterparty budgets #2032's layer).

    SSO → ID binding (the part that must not be hand-waved)

    Google identity and Iroh ID are different kinds of thing — one is an authentication relationship with Google, the other is a self-sovereign key — and the design keeps them in separate lanes:

    • The account record keys on Google's sub (the stable subject identifier), never the email address (emails change, get recycled, and are shared in ways sub is not). Email is stored, if at all, as contact metadata.
    • Binding ceremony: signed-in user proves control of their Iroh root key by signing a service-issued challenge with it (in-browser via the web client, or from their own node). The result is a binding record: {google sub, Iroh ID, bound at} countersigned by the Iroh key and attested by the service. Sign-in alone never creates a binding; a Google account with no key proof is an empty hosted account that can claim nothing.
    • Devices don't bind individually: the binding names the root/person ID; devices join through the platform's existing device-grant mechanism (groups layer), exactly as in the self-hosted model.
    • Recovery is where bindings are attacked, so it is deliberately slow: losing Google access is a Google problem (their recovery), and the binding survives it — regained Google access re-enters the same account. Losing the Iroh key is the hard case: Google sign-in alone must never silently rebind the account to a fresh key (that converts any Google account takeover into an ID takeover). Rebind requires the old key's signature, or a delayed rebind: request → notification to the old binding's contact points → waiting period (parameter; days, not minutes) → rebind, with the rebind itself published as a signed service record. Users who keep a self-hosted node holding their key never need this path.
    • Honesty about the trust trade: hosted storage is operator-readable at rest unless the user chooses client-side encryption (keys never leave their devices; the DO then stores opaque objects and hosted search/index features degrade to what metadata allows). The hosted tier sells availability and convenience, not self-hosted confidentiality — the offer must say so in those words, and the encryption option must exist, or "hosted ID" quietly becomes "trust us with everything."

    Shape comparison (per-user cost drivers)

    Shape What it is Idle cost Marginal cost drivers Fit
    A. Static site + local storage Web client only; data on the user's device; P2P sync when peers meet ~zero (asset hosting) Bandwidth for app assets only The free floor. No availability guarantee; device loss = data loss unless synced. Every hosted user should be able to fall back to this — the client is the same.
    B. Per-user Durable Object + SQLite + R2 (recommended hosted tier) #2044's design: DO per user, hibernating WebSockets, blobs in R2 ~zero (DO storage only, GB-months) Requests / live-session time, stored GB, R2 volume. All meterable per user from the DO itself The hosted offer. Structural isolation, self-limiting blast radius, exportable (below).
    C. Websites-with-backends (apps/worlds) Per-world/app-instance DOs rather than per-user; shared multi-tenant Worker front ~zero idle Per-world request/storage costs, attributed to the world (and its owner/sponsor), not to each participant Complement to B, not an alternative: users are per-user (B); shared things are per-thing (C). A single shared multi-tenant DB for user stores was considered and rejected — it trades away B's structural isolation to save money that B's idle-is-free already saves.
    D. Container per user Always-on small server per user Non-zero forever Flat per-user monthly, idle or not Violates Drew's guardrail — rejected as a tier. Containers remain legitimate only as ephemeral, on-demand jobs (heavy world compute, bulk import/export): started per task, time-boxed, auto-stopped, cost attributed to the job — "a little container use" in the literal sense.

    Cost guardrails (making "no indefinite container spend" a system property, not a hope)

    1. Idle = $0 is architectural in shapes A–C (hibernation, no listeners, no per-user processes); anything that would run while a user is away needs explicit justification as a shared, multi-tenant job.
    2. Per-user metering + budgets: each DO meters its own requests/storage; free-tier ceilings degrade gracefully (read-only + export prompt at storage cap, never silent data loss) and paid tiers raise the same numbers — the id: investigate: comms abuse controls — blocks, rate limits, and per-counterparty budgets #2032 budget pattern applied to hosting.
    3. Ephemeral-container rule: any container work names an owner, a max runtime, and an auto-stop at spawn; a container found running without all three is a bug to file, not a pattern to copy.
    4. Exit is a feature: the full store exports as the user's signed objects (self-authenticating by construction) plus the binding record, importable into a self-hosted node or another host. Portability is cheap because the data model is signed objects — and it is the credible answer to "why trust your hosted tier": you don't have to, and leaving is a download, not a negotiation.

    Open for the synthesis (#2046)

    Exact free/paid tier numbers and the rebind waiting-period length; whether client-side encryption is default-on for hosted stores (recommendation: offer at binding time with the feature trade explained, default off only because of the search/index cost — flag as a genuine values call for Drew); R2 dedupe vs per-user encryption interaction (encrypted blobs dedupe only per-user — accept it); the Shape C cost-attribution rule for worlds whose owner lapses.

    Follow-on routing (same pass)

    🤖 Agent note · done — queue worker (repo-worker-code), lane C, task 3354. Investigation recorded here; spec follow-on filed + enqueued; #2046 inputs complete.

  9. 3 remaining items

  10. added
    source/creatorFiled by the new-issue-creator job
    task/triageQueue packet kind: verify, reproduce, and route this item
    on Nov 3, 2026
  11. developing-today commented on Nov 3, 2026

    @developing-today
    Contributor

    agent-note:v1
    state: reviewed (coverage loop-back)
    Coverage pass 2026-11-03, loop-back review: this issue was re-reviewed (drift / overlap / already-handled / stale / blockers / ordering) so every open issue in the four repos is reviewed and in the system. Verdict: still live; in the system as task 7273 (triage, queued, priority 50) in repo-health-code-pilot. Labels audited and synced. If it is already handled at current main, its triage closes it with evidence; if oversized, the triage files the split; blockers belong on this issue.
    Found via: coverage loop-back 2026-11-03

  12. added
    work/assignedQueue worker lifecycle state
    queue/claimedQueue worker lifecycle state
    and removed
    queue/queuedHas a queued task in the Supabase agent queue
    on Nov 3, 2026
  13. dezren39 commented on Nov 3, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · picked up

  14. dezren39 commented on Nov 3, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · done

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

app/idPackage/app: id (pkgs/ or apps/ in this repo)priority/highImportant and time-sensitivesource/creatorFiled by the new-issue-creator jobtask/triageQueue packet kind: verify, reproduce, and route this item

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions