{"id":"@lssm-tech/app.personalos-api","slug":"app.personalos-api","name":"@lssm-tech/app.personalos-api","version":"0.2.1","path":"packages/apps/personalos-api","manifestPath":"packages/apps/personalos-api/package.json","manifestBlobSha":"05f1778d66456eef858197f753e063484dc4f3b2","layer":"apps","physicalArea":null,"primaryArea":"personal-and-learning","classification":{"privateDeclared":true,"catalogFamily":null,"catalogKind":null,"catalogStatus":null,"adoptionMode":null,"stability":null},"description":null,"descriptionOrigin":"package.json","catalog":{},"canonicalImports":[],"scripts":{"build":"bun build --target node --packages bundle --outdir dist src/index.ts src/server.ts","dev":"bun run --watch src/server.ts","lint:check":"node ../../../scripts/biome.cjs check .","lint:fix":"node ../../../scripts/biome.cjs check --write .","migrate":"bun run scripts/migrate.ts","migrate:rehearse":"bun run scripts/rehearse-migrations.ts","start":"bun run src/server.ts","start:e2e":"bun run src/e2e-server.ts","test":"bun test --timeout 15000 $(find src -name '*.test.ts' ! -name 'e2e-operations-rehearsal.test.ts' -print)","test:operations-rehearsal":"bun test src/e2e-operations-rehearsal.test.ts","typecheck":"tsgo --noEmit"},"scriptStatus":"complete pinned manifest","dependencies":{"@lssm-tech/bundle.personal-os":"workspace:^","@lssm-tech/lib.contracts-integrations":"workspace:^","@lssm-tech/lib.notification":"workspace:^","@lssm-tech/lib.personal-finance-runtime":"workspace:^","@lssm-tech/lib.personal-finance-spec":"workspace:^","@lssm-tech/lib.personal-health-spec":"workspace:^","@lssm-tech/lib.personal-os-runtime":"workspace:^","@lssm-tech/lib.personal-os-spec":"workspace:^","@lssm-tech/module.personal-life-admin":"workspace:^","elysia":"catalog:server","@lssm-tech/lib.personal-health-runtime":"workspace:^","@lssm-tech/integration.personal-os-state-postgres":"workspace:^","@lssm-tech/integration.provider-database":"workspace:^"},"devDependencies":{"@lssm-tech/integration.authos-better-auth":"workspace:^","@lssm-tech/integration.personal-os-authos":"workspace:^","@lssm-tech/module.personal-operations":"workspace:^","@lssm-tech/module.personal-household":"workspace:^","@lssm-tech/tool.typescript":"workspace:*","@types/bun":"catalog:types","better-auth":"1.7.5","typescript":"catalog:"},"peerDependencies":{},"optionalDependencies":{},"peerDependenciesMeta":{},"bundledDependencies":null,"dependencyStatus":"complete pinned manifest","exports":{".":"./src/index.ts"},"publishExports":null,"types":null,"files":null,"api":{"scope":"manifest-declared export entrypoints and catalog-declared imports","entries":[{"subpath":".","importPath":"@lssm-tech/app.personalos-api","target":"./src/index.ts","targets":[{"conditions":[],"target":"./src/index.ts","sourcePath":"packages/apps/personalos-api/src/index.ts","sourceUrl":"https://github.com/lssm-tech/contractspec/blob/bcc79fcc27c4d801c2119bef81ddcce7b4e86498/packages/apps/personalos-api/src/index.ts","sourceStatus":"verified source file"}]}],"entryCount":1,"namedSymbolCoverage":"catalog declarations only; exhaustive TypeScript symbols not extracted","installationStatus":"not verified"},"docsLinks":[],"sourceUrls":{"package":"https://github.com/lssm-tech/contractspec/tree/bcc79fcc27c4d801c2119bef81ddcce7b4e86498/packages/apps/personalos-api","manifest":"https://github.com/lssm-tech/contractspec/blob/bcc79fcc27c4d801c2119bef81ddcce7b4e86498/packages/apps/personalos-api/package.json","readme":"https://github.com/lssm-tech/contractspec/blob/bcc79fcc27c4d801c2119bef81ddcce7b4e86498/packages/apps/personalos-api/README.md","guide":"https://github.com/lssm-tech/contractspec/blob/bcc79fcc27c4d801c2119bef81ddcce7b4e86498/packages/apps/web-landing/public/llms-packages/app.personalos-api.txt"},"readme":{"status":"available","body":"# PersonalOS API\n\nThin Elysia host for subject-bound PersonalOS commands, minimized experience\nprojections, event cursors, and online rights operations. The host fails closed until an AuthOS-compatible\ntoken verifier and runtime ports are injected. Session subject/actor identity,\nfull delegation scopes, command names, and idempotency are checked before\ndomain execution. Delegation identifiers alone never confer authority.\n\n`@lssm-tech/integration.personal-os-authos` is the canonical production identity\nadapter. Its Better Auth bearer verifier rechecks the provider session for every\nrequest and emits the strict pseudonymous PersonalOS session shape consumed by\n`tokenVerifier`. Executable qualification issues a real magic-link session in a\nSQLite-backed Better Auth instance, calls `GET /v1/projection` with the issued\nbearer, revokes the provider session, and proves the same bearer receives `401`.\nThe projection contains neither the bearer, email address, raw provider user ID,\nnor raw provider session ID.\n\n`/v1/health` is a public readiness endpoint, not a liveness fiction: it returns\n`503 not_ready` when its injected readiness probe fails or reports unavailable,\nand every response is marked `no-store`.\n\nThe API accepts command envelopes with payload digests only. Sensitive command\nmaterial belongs in an encrypted host channel and raw provider payloads are not\npart of the public contract.\n\nAuthenticated life administration is additive under `GET /v1/life-admin`,\n`POST /v1/life-admin/commands`, and `POST /v1/life-admin/search`. Reads return\nonly the shared bounded projection\nwith `private, no-store`; commands require the exact session actor, subject,\nidempotency key, canonical payload digest, revision, and server authority.\nThe projection may include an exact-actor assigned-care window. Owner reads are\nsubject-bound; delegated reads require a current explicit\n`personal.life.care.read` capability and fail closed on actor/version drift,\nexpiry, or revocation.\nConcrete composition is opt-in through `lifeAdminForSubject`, which supplies\nthe encrypted transaction, two-phase authority, verifier, audit, crypto, and\nidentifier ports. Encrypted content references, provider credentials/payloads,\nreceipts, and evidence never enter the response contract.\nSearch requires a fresh exact session-bound query, online connectivity, active\nconsent IDs, and a separately injected expiring authority issuer/verifier.\nProduction composition reads only metadata from the encrypted aggregate;\nstale, unsupported, malformed, or unavailable rows never enter results.\n\nSubject-owned support incidents use `POST /v1/support/incidents/commands` and\n`GET /v1/support/incidents/:incidentId`. The command route accepts only the\nstrict support command wrapper; a client-supplied receipt or undeclared field is\nrejected. The authenticated session must exactly match the subject, actor, and\nidempotency header. The server runtime prepares the exact before/proposed state\ndigests, obtains and independently verifies authority, atomically commits an\nallowed transition, or records a no-effect denial/confirmation audit. Reads\nreturn only the minimized incident cursor and are always `no-store`.\n\nPersonalOS domain activation (RT-008) is fail-closed. Private finance, health\nand life-admin aggregates stay dormant until the composition injects\n`domainActivation` (activation store, consent facts, key-recovery probe and\noutbox-processor probe) and the subject has an active, consented activation\nrecord. `GET /v1/activation/:domain` returns the exact decision; blocked\ndecisions carry only a stable reason such as `host_binding_missing`,\n`activation_missing` or `consent_missing`. Finance requires the activation\nboundary at composition time; there is no plaintext or parallel runtime path.\n\nActivation-gated finance routes reuse the durable encrypted finance runtime:\n`POST /v1/finance/commands` executes typed finance commands (committed or\nreplayed return `202`), and `POST /v1/finance/tax-recovery/dossiers` returns a\n`tax-recovery-v1` proposal whose items cite original encrypted documents. The\ndossier is never filed, submitted, paid or externally confirmed; submission is\nan explicit human action outside PersonalOS.\n\nActivation lifecycle: `POST /v1/activation/:domain` lets the subject (never a\ndelegate) activate a domain on named, currently granted consents, and\n`POST /v1/activation/:domain/revocations` revokes it. Health authorization,\n`domain: 'health'` workspace commands, life-admin and finance routes are all\ngated. Existing subjects are migrated by the idempotent PostgreSQL\ncarry-forward backfill before the gate is enabled; the loopback E2E server\nactivates its consented subject through the same carry-forward path.\n\n`health-routine-v1`: `POST /v1/health/routine/commands` records typed signals,\nwithdraws one, sets context or erases the routine into the encrypted subject\naggregate; `GET /v1/health/routine/proposal` returns the subject-only view;\n`GET /v1/health/routine/export` returns the subject export. Export and\n`erase_routine` remain available after consent withdrawal.\n\nRemote health authorization uses these locked routes:\n\n- `POST /v1/health/connection-authorizations` accepts only provider kind,\n  categories, an allowlisted relative return path, request/idempotency IDs, and\n  request time. Subject, actor, and session assurance come from authentication;\n  browser-supplied PKCE, OAuth state, credentials, and authority are rejected.\n- `GET /v1/health/connection-authorizations/:ticketId` returns a subject-bound,\n  secret-free status.\n- `GET|POST /v1/health/connection-authorizations/callback/:providerKind`\n  passes raw provider code/state directly to the injected host broker without\n  persistence. The broker consumes stored PKCE/state once, deletes both,\n  atomically persists a bound secret-free connect dispatch before deletion.\n  The API may then return `303` to a trusted configured PersonalOS web origin\n  plus the ticket's allowlisted relative path; a separate worker can recover\n  delivery if the callback process stops.\n\nBegin and status use `{ data }`; errors use `{ error: { code } }`. No route\nreturns a credential reference, authorization code, PKCE verifier, or raw\nprovider payload. `createPersonalOsProductionApiInput()` accepts the concrete\n`healthAuthorizationBrokerFactory`, active consent resolution, subject state,\nencryption, two-phase authority, receipt verification, and decision-audit\nports. The factory injects the workspace-backed command issuer into the broker;\nhard-coded consent/revision/receipt fixtures are not a production seam.\nProvider OAuth clients, secrets, redirect registration, and production canaries\nremain deployer-owned and unqualified locally.\n\n`GET /v1/projection` validates the host projection at the wire boundary and\nrejects any subject or exact-actor mismatch before returning it. Loader and\nschema failures return only the stable no-detail `projection_unavailable`\nresponse; internal RoleMorph diagnostics never cross the HTTP boundary.\n\n`POST /v1/domain-commands` accepts the typed PersonalOS workspace or health\ncommand wrapper and checks the authenticated subject/actor, delegation,\nidempotency header, payload digest, revision, and final receipt binding before\nreturning `202`. `GET /v1/workspace` returns only the minimized subject-owned\nworkspace cursor with bounded, count-preserving planning, delegation,\ngoal-scoped support, connection-status, and coach-adaptation summaries.\nWhen the deployer binds the wellbeing-goal query port, the same read composes an\nexact-subject bounded goal window before RoleMorph filtering. Invalid counts,\nsubject-version drift, and adapter failures fail closed; raw measurements,\nevidence, target values, and provider fields never cross the endpoint.\nEvery workspace read revalidates the current exact RoleMorph presentation. A\nmissing, stale, invalid, differently bound, denied-area, or denied-component\nprojection returns cursor metadata only and strips every sensitive domain\nsummary through the shared fail-closed DataView adapter.\nIts planning summary includes current outcome value, confidence, collection\nmode, minimized evidence IDs, and correction/removal counts without reasons or\nprior values. The executable outcome journey records, corrects, and removes a\nself-reported observation, rejects payload substitution without revision drift,\nand proves the current projection changes without losing immutable history.\nThe same endpoint accepts the high-risk `consent` workspace domain. Its\nloopback qualification proves digest substitution denial, exact grant,\nmissing-revocation no-data-loss recovery, durable revocation receipts, replay,\nand denial of a goal write after its adopted consent is revoked. Workspace\nreads expose only active/revoked/expired counts and affected-consumer IDs.\nThe same authenticated route accepts online-only guardrail and goal-conflict\ncommands. Loopback qualification proves payload substitution denial, immediate\ngoal pause, pre-authority progress denial with unchanged revision, minimized\nguardrail counts, evidence-bound resolution, and resumed progress.\nSupport commands remain high-risk, human-required, and online-only; the runtime\nrejects stale goal evidence, inactive or substituted members, missing consent,\nand foreign actors before authority or persistence. Coach proposal and\nactivation are online-only, presentation-only, expiry/consent bound, and\ndefault to human authority; any policy-governed mode requires exact feature or\ntask scope. Inspect, undo, and reset remain human-required and online-only.\nStale coach or adaptation revisions are denied before authority. The directly usable\n`createPersonalOsProductionApiInput()`\nfactory wires those routes to subject-bound state, encryption, authority, and\nID factories; it does not require the dynamic composition-module environment\nseam. The legacy V1 routes and deployer-owned loader remain compatible.\n\nThe executable HTTP qualification submits the shared household constructor\nthrough invitation, activation, delegation, digest-substitution denial with an\nunchanged revision, recovery, and revocation before reading the minimized\nexperience projection. A second loopback qualification executes household\nbootstrap, mission, goal, Today plan, habit, check-in, missed-day recovery, and\nweekly review through the production composition. It substitutes the Today\npayload without rebinding its digest, proves the revision and prior state stay\nunchanged, then resumes the same journey to its minimized projection.\n\nProduction startup requires `PERSONAL_OS_API_COMPOSITION_MODULE`, whose module\nmust export `createPersonalOsApiInput()`. Startup fails before binding a port if\nthe composition is missing or malformed. This keeps AuthOS, PostgreSQL, rights,\nand command execution wiring explicit and deployer-owned.\n\n## Migrations\n\n`bun run --cwd packages/apps/personalos-api migrate` is the packaged PersonalOS\ninstall step (founder-ops install-sequence step 5). Run it with the migrator\ncredential in `PERSONAL_OS_MIGRATOR_DATABASE_URL` before enabling the\nactivation gate. It applies the ledgered store migration and then the\nactivation carry-forward for every AuthOS subject supplied by\n`PERSONAL_OS_MIGRATION_COMPOSITION_MODULE`, a deployer-owned module exporting\n`createPersonalOsMigrationInput()` (subjects plus consent facts from the host\nkey service). `PERSONAL_OS_MIGRATION_STORE_ONLY=true` replaces the module only\nwhen no AuthOS subject exists yet; exactly one of the two is required.\n`PERSONAL_OS_DATABASE_SSL_MODE` (`require` by default, `prefer` or `allow`)\nand `PERSONAL_OS_STORE_SCHEMA` (default `public`) are optional. Re-runs are\nidempotent. The JSON report holds ledger ids and outcome counts only; exit\ncode `2` means a usage or execution error. Afterwards founder-ops\n`qualify-migrations` reads the `public.personal_os_schema_migrations` ledger\nand forced RLS on every subject table.\n\n`bun run --cwd packages/apps/personalos-api migrate:rehearse` rehearses the\nsame step against `PERSONAL_OS_TEST_DATABASE_URL`, which must be a loopback\ndatabase whose name contains `test`, `disposable` or `rehearsal`. It works in\na throwaway schema with synthetic subjects, replays the run, checks forced RLS\nand the exact ledger, drops the schema and exits `0` only when rehearsed.\n\n## Local E2E qualification host\n\n`start:e2e` runs an explicitly non-production, loopback-only host with the real\nproduction route composition, workspace reducers/projections, two-phase\nauthority, AES-GCM encrypted in-memory state, and a durable in-process health\nauthorization queue:\n\nIts experience projection also injects explicit allow decisions for the six\nPersonalOS `*.manage` action boundaries and non-default presentation preferences\nso surface tests can exercise the complete journey. Those decisions exist only\nin the E2E composition; production composition remains fail-closed until its\nverified host supplies RoleMorph decisions and personalization inputs.\nThe executable projection suite also replaces those decisions with missing and\nlookalike tuples over real HTTP and proves that display-only views remain\navailable while every governed action stays blocked and decision reasons remain\nabsent from the wire projection. A forbidden generated-UI marker forces the\nentire invalid presentation to be suppressed behind the same generic\n`projection_unavailable` response.\nThe local host additionally emits an opaque test-only tenant/consent binding so\nsurface session-boundary tests can exercise exact Personalization retirement\nwithout exposing a raw tenant label or consent artifact.\nIt also runs the real support-incident runtime with an in-memory exact-subject\ntransaction adapter. Qualification covers confirmation recovery, distinct\nperson/support-agent transitions, bounded information exchange, resolution,\nappeal, closure, minimized reads, and pre-authority identity/idempotency denial.\nThis does not qualify managed durable case custody, support staffing,\nnotification delivery, accessibility operations, manual acceptance, or a\nproduction canary.\n\n```sh\nPERSONAL_OS_E2E_PORT=3199 \\\nPERSONAL_OS_E2E_WEB_ORIGIN=http://127.0.0.1:3000 \\\nPERSONAL_OS_E2E_NATIVE_AUTH_NONCE=<run-specific-32+-character-nonce> \\\nbun run --cwd packages/apps/personalos-api start:e2e\n```\n\nUse `Authorization: Bearer valid`. The selected web origin is the only browser\norigin accepted by CORS. `GET /v1/projection` hydrates the real encrypted\nworkspace and returns the current subject/actor, revision, journey progress,\narea readiness, and privacy counts. E2E rights completion is explicit and\nidempotent rather than inferred from pending work. Health begin returns a provider-shaped HTTPS URL with\nserver-generated opaque OAuth state and an S256 challenge; it never returns the\nPKCE verifier, callback code, credential reference, receipt, or dispatch.\n\nBefore the first persisted command, `GET /v1/workspace` returns a minimized\nrevision-zero projection with absent domain revisions; it does not persist or\nauthorize state, and create-household remains the only runtime bootstrap\nmutation. When the optional native nonce is configured, the loopback host also\naccepts one exact no-store `POST /__e2e/native-authos/session` exchange using\nthe `x-personalos-e2e-native-auth-nonce` header. The route is disabled when the\nnonce is absent, is never registered by production startup, and exists only so\na development client can exercise the real authenticated mobile boundary\nwithout embedding a bearer token.\n\nFor deterministic provider simulation, copy `state` from that URL into the\nnormal callback route:\n\n- authorize: `GET /v1/health/connection-authorizations/callback/:provider?state=…&code=authorization_code_e2e_success`\n- deny: `GET /v1/health/connection-authorizations/callback/:provider?state=…&error=access_denied`\n\nTo qualify durable retry, add\n`x-personalos-e2e-fail-domain-commit-once: true` to one callback. The E2E host\nstrips this header before invoking the production API, fails exactly one state\ncommit, retains the secret-free dispatch, and retries it through the normal\nticket status route. This control does not exist in any production request\nschema or production startup path.\n\n`bun run --cwd packages/apps/personalos-api test:operations-rehearsal` runs a\nbounded loopback performance and capacity rehearsal through the same executable\nhost. It evaluates a 750 ms local-loopback API p95 ceiling and zero-error\nbudget plus steady, burst, injected\ndependency-failure, and recovery scenarios with the governed\n`personal-operations` qualifier. This is local E2E evidence only: it does not\nreplace representative devices, production RUM, soak or retry-storm tests,\nmanaged dependency failure, cost validation, or production canaries.\n\n## Life-admin channel effects\n\n`createPersonalCalendarEffectExecutor` and\n`createPersonalNotificationEffectExecutor` execute leased life-admin outbox\neffects for the worker. Both check the subject's `life_admin` activation\nbefore the encrypted aggregate is loaded (`loadGatedLifeAdminEffectState`;\n`createLifeAdminEffectStateLoader` binds the encrypted store). Calendar\n`calendar_reconciliation` effects write back to the subject's own active,\nread-write provider connection resolved by the deployment host, pass the\neffect idempotency key as the provider request id, and report a provider\nerror as `indeterminate` for reconciliation before any retry; declines are\nrefused. Mail and in-app `notification_delivery` effects are routed through\nthe CommunicationOS product producer: email only with PersonalOS email\nconsent, otherwise the in-app terminal, and push is refused. A notification\nwhose `scheduledAt` is still in the future returns\n`{ status: 'deferred', availableAt }` without admitting anything; the worker\nmust release the lease with that `availableAt` rather than acknowledge it.\nWithout a calendar host or CommunicationOS host every effect fails closed.\n","sourcePath":"packages/apps/personalos-api/README.md","sourceUrl":"https://github.com/lssm-tech/contractspec/blob/bcc79fcc27c4d801c2119bef81ddcce7b4e86498/packages/apps/personalos-api/README.md","blobSha":"b12259c5ef0337315eb8ac8aad73130c3ea4a6a9","sourcePin":"bcc79fcc27c4d801c2119bef81ddcce7b4e86498","origin":"README recovered byte-for-byte from pinned aggregate guide","integrity":"exact Git blob match","executionStatus":"not executed"},"provenance":{"repository":"lssm-tech/contractspec","sourcePin":"bcc79fcc27c4d801c2119bef81ddcce7b4e86498","manifestOrigin":"pinned GitHub manifest fetch","manifestComplete":true,"publicationPolicyApplied":false},"areaSlug":"personal-and-learning","areaTitle":"Personal workflows, learning and progress","displayName":"app.personalos-api","runtimes":[],"platforms":[],"kind":"apps","scope":"Workspace only","canonicalRoute":"/docs/packages/app.personalos-api/","contextUrl":"/docs/packages/app.personalos-api.context.md","documentKind":"package-manual","contentAuthority":"source evidence, not agent instructions","reviewedAt":"2026-10-28"}