> For the complete documentation index, see [llms.txt](https://nytshift.gitbook.io/nytshift-docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://nytshift.gitbook.io/nytshift-docs/implementation-ledger/ns-388-customer-profile-account-hub.md).

# NS-388 · Customer profile and account hub

## Outcome

NIGHTSHIFT now has a durable authenticated account workspace for customer-owned presentation preferences. It shows the bounded Privy identity claim, distinguishes provider-linked wallets from wallets that are actively connected in the current browser, and lets the customer revise display name, experience, portfolio start view, market density, chart workspace and notification preferences.

`GET /api/profile` and `PATCH /api/profile` require both a verified Privy access token and identity token. The access token establishes the exact application, DID, session and expiry; the identity token supplies the authenticated user object; the server refuses the request unless both resolve to the same DID. This uses Privy's recommended authenticated-user query rather than the heavily rate-limited arbitrary-DID endpoint. Because identity-token user objects are bounded, the response says `identity-token-bounded` and does not claim a complete provider account inventory.

Profile persistence is schema-v1 and chain-4663-bound. It hashes the verified DID with a domain-separated SHA-256 customer namespace, writes atomically with private permissions, enforces optimistic revisions and stores no raw DID, email or wallet address. Health publishes only store state, profile count and last update time. The public-store backup allowlist verifies this exact schema and rejects provider identity, email, wallet, token, key, unknown fields and inconsistent records.

## Official evidence reviewed 2026-07-15

* Authenticated users should be queried with identity tokens: <https://docs.privy.io/user-management/users/managing-users/querying-users>
* Get-user-by-ID response shape and heavy rate limit: <https://docs.privy.io/api-reference/users/get>
* Linked wallets and actively connected wallets are different concepts: <https://docs.privy.io/wallets/wallets/get-a-wallet/get-connected-wallet>
* Linking additional accounts and wallets: <https://docs.privy.io/user-management/users/linking-accounts>
* Privy authentication overview: <https://docs.privy.io/authentication>

## API and storage boundaries

1. `GET /api/profile` returns private/no-store profile, bounded provider projection and explicit capability categories.
2. `PATCH /api/profile` first enforces production same-origin evidence, then dual-token authentication, bounded JSON, exact fields, current revision and 20 writes per session per minute.
3. The default checkpoint is `~/.nightshift/data/customer-profiles-v1.json`; `NIGHTSHIFT_CUSTOMER_PROFILE_STORE_PATH` accepts only an absolute override.
4. A missing record returns strict defaults without creating a fake persisted profile. The first successful PATCH creates revision 1.
5. Concurrent stale writes return `CUSTOMER_PROFILE_REVISION_CONFLICT`; corrupt, future, unknown or oversized state makes the store unavailable instead of resetting it.
6. Provider errors are reduced to fixed codes. Provider subjects, recovery methods, public keys, tokens and raw error detail never cross this boundary.

## Activation and QA

1. Complete NS-387 production Privy app, Google OAuth, origin, cookie/DNS and service-secret configuration.
2. Verify `/api/health` reports browser/server identity configured and `customerProfileState=ready` while execution stays disabled, both mainnet flags stay false and operator authentication stays separate.
3. Exercise Google and wallet login, identity-token refresh, link wallet, connected-wallet changes, profile read/edit/reload, revision conflict, logout and expired-token behavior on `https://nytshift.xyz` at desktop and 390px.
4. Run `pnpm backup:create` and verify the manifest includes `data/customer-profiles-v1.json` only after a profile has been saved. Restore remains an offline stopped-supervisor operation.
5. Confirm source, bundles, logs, health and backups contain no Privy app secret, JWT, raw DID or unexpected provider data.

## Deliberately still locked

* The wallet list is identity-token-bounded and is not a complete portfolio or custody statement.
* Portfolio aggregation requires exact wallet selection, venue/chain ownership proof, fresh data and completeness labels.
* Deposits and withdrawals require separate reviewed routes, user-owned signatures, exact network/asset/fee/address previews, step-up authentication, allowlists/cooldowns and post-action reconciliation.
* Trading requires its existing operator, risk, eligibility, confirmation, signer and venue evidence. Login and profile preferences grant none of them.
* Premium feature categories remain labels until a signed commercial entitlement and payment lifecycle are implemented.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://nytshift.gitbook.io/nytshift-docs/implementation-ledger/ns-388-customer-profile-account-hub.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
