Skip to content

Promote hosted accounts to canonical identity and fix spoofable owner authorization #23

Description

@harrymove-ctrl

Context

Hosted identity is effectively a self-asserted ownerId query param / body field defaulting to "anonymous" (apps/api/src/worker.ts:273/1704/1742), so GET /api/schedules?ownerId=<someone-else> and GET /api/alerts?ownerId=... (worker.ts:1704/1742) return another user's data, and namespace ownership is checked against the same untrusted value (worker.ts:1125). The contextmem_accounts + contextmem_sessions tables already exist (migrations/0001_account_gate.sql) but the hosted Worker never uses them for identity; authed users are even hardcoded unlimited: true (worker.ts:1503).

Goal / user story

As a hosted user, I want my owner-scoped data (namespaces, schedules, alerts, usage) keyed to a verified session/account so no one can read or mutate my resources by guessing my ownerId.

Acceptance criteria

  • A resolveOwner(request, env) helper derives owner_id from a verified session (contextmem_sessions.token_hash via httpOnly cookie or Authorization: Bearer ctx_…) — never from a query/body param — and is the single source for all owner-scoped routes.
  • Owner-scoped routes (/api/schedules, /api/alerts, /api/namespaces, /api/extractions, /api/usage) reject requests whose resolved owner != the row owner with 401/403; the ?ownerId= param is ignored for authorization.
  • Delegate import (/api/memwal/import-delegate) associates the delegate with an account row (reuse delegate_key_ciphertext, accounts table) rather than standing alone.
  • quota.unlimited (worker.ts:1503) is replaced by a per-account/plan lookup (default plan = limited) so quotas become enforceable.
  • Anonymous/demo callers keep working via the existing demo:<ip> owner (worker.ts:4317) but are confined to demo-scoped resources only.
  • Tests: cross-owner read is denied; a valid session reads its own rows; an expired/absent session falls back to anonymous with no access to owned rows.

Implementation notes

Use the local Fastify model (LocalAccountStore, hashed ctx_ sessions) as the reference for the hosted version; hash session tokens with the same scheme as namespace tokens. Keep changes backend-only and additive: add session middleware in the router (worker.ts:577+) ahead of owner-scoped branches. Out of scope (handled by the onboarding theme): signup/login UX, managed-delegate first-write flow, email/passkey/wallet — this issue only makes authorization trustworthy. Org/tenant_id can be a nullable forward-compat column; multi-tenant RBAC is out of scope. Add a migration only if new columns (e.g. plan, tenant_id) are needed.

Sui Overflow angle

Trustworthy owner identity is the off-chain anchor that later binds to a Sui-owned Namespace object / Seal policy (roadmap §2 registry) — without it, an on-chain ownership claim would attest a spoofable value. It also closes a real cross-owner data-leak bug before the repo goes public for the hackathon.

Dependencies

Unblocks per-account quotas (rate-limit issue) and owner-scoped /api/usage (ledger issue). Deliberately does not overlap the onboarding theme's signup-UX work.

Part of the ContextMEM roadmap (#4) • Sui Overflow build.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P1Important: hardens the demo and core productfeatureUser- or agent-facing capabilityplatformBackend platform plumbing: Worker, D1, queues, secrets, metering

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions