Global data root (~/.openalice) + sealed broker credentials#324
Merged
Conversation
Preparation for moving the default user-data home off cwd: a global vitest setup file pins OPENALICE_HOME to a per-worker temp dir so module-level dataPath() constants never resolve into the developer's real data root, and the smoke harness pins the spawned Guardian to the checkout so its SOFT restart-flag check keeps testing the same path it watches. session.spec cleanup now sweeps the same dataPath the store writes to (its cwd HACK comment was stale — SessionStore already goes through dataPath). No behavior change for production code; e2e config deliberately not wired (it reads the real credential store by design). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… through dataPath() Guardians (dev.ts, prod.mjs) now inject OPENALICE_HOME — the variable src/core/paths.ts actually reads — instead of OPENALICE_USER_DATA_HOME, which nothing in src/ consumed; dev children only worked via cwd inheritance. prod.mjs keeps the legacy name as a deprecated fallback for one release. Dockerfile drops the duplicate. Eight call sites hardcoded cwd-relative 'data/...' paths instead of dataPath(), so they silently split state from the configured root under any non-cwd OPENALICE_HOME (e.g. packaged Electron): wipeUTATradingData, the UTA restart-flag trigger, event-log, tool-call-log, cron store, news store, snapshot store, and the order-sentinel script. No behavior change while the default home is still cwd; this is the ground work for moving it to ~/.openalice. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
One global store shared by every topology — pnpm dev, bare pnpm start, and the packaged app — so broker credentials and trading state are configured once, not re-entered per checkout. Joins the existing ~/.openalice conventions (workspaces/, provider-keys.json); the data/ subtree under it stays the portable unit. Adoption is deliberately manual: when a checkout still carries a legacy ./data store and the global one is virgin, guardian dev and Alice's boot path print a one-line mv instruction instead of auto-moving — multiple worktrees may each have a ./data and only the user knows which is canonical. OPENALICE_HOME="$PWD" pnpm dev pins a checkout-local store for experiments that shouldn't touch real data. Docker is unaffected (OPENALICE_HOME=/data stays explicit); Electron relocation of existing stores lands separately. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… launch The Electron shell joins the global-root story: OPENALICE_HOME points at ~/.openalice in both packaged and dev branches, and an existing <userData>/data store from pre-global-root installs is moved there once — before ports.json is read from the new root and before the backend boots (which would run migrations against an empty store). Relocation moves ONLY userData/data (userData also holds Electron's browser-profile state, which stays). Same-volume = atomic rename; cross-volume = copy to a tmp dir then atomic commit, legacy kept as data.relocated — never deleted. Idempotent across boots; a stale partial copy from a crashed run is swept and retried. On failure the shell surfaces a dialog and quits rather than forking the user's trading history next to the real store. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…-bound key) data/config/accounts.json — the broker credential store — is now an AES-256-GCM envelope instead of plaintext JSON. The key is a random 32-byte secret at <userDataHome>/sealing.key (0600, auto-generated on first write, zero user interaction), deliberately OUTSIDE the portable data/ subtree: backing up, syncing, or sharing data/ no longer carries the material needed to read the credentials inside it. Honest scope: this does not defend against same-user malware or a compromised Alice process — the structural answer there remains the detached-UTA split. The envelope is versioned so the key can later move into an OS keychain without a format break. Every write funnels through one sealed 0600 writer; reads accept the legacy plaintext array, and migration 0009 reseals existing stores eagerly at first boot so plaintext doesn't linger until the next config change. An unreadable store (data/ copied without its key) is quarantined loudly — never deleted — and the app boots with an empty store instead of crashing. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
GET /api/trading/config/ already masked secrets, but the create and edit responses echoed the full validated record — plaintext broker credentials — back over the wire. Both now return maskSecrets(...), which also makes the client's local state shape-identical to what GET returns (the edit round-trip already restores masked values server-side via unmaskSecrets, so the UI needs no change). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
CLAUDE.md (data/ entry), README (config paths, token rotation, persona override), and safe/knowledge/config-files.md (path resolution, file permissions, accounts.json at-rest shape, recon commands) now describe the global user-data root, the OPENALICE_HOME override, and the sealed credential store. Factual path updates only — no positioning copy touched. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
The wizard's onSave chained createUTA → reconnectUTA and only closed the dialog when the first broker connection succeeded. A failed or slow first connect left the dialog open over an ALREADY-persisted account — the user reads it as "save failed", clicks Save again, and gets a 409 duplicate warning for their trouble. Persist → close. The first connection now runs in the background where the list's health badge already tracks it (Reconnecting → Connected / Offline), and credential validity keeps its own explicit Test step in the wizard. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
~/.openalice— one store shared bypnpm dev, barepnpm start, and the packaged app; broker accounts get configured once, not per checkout.OPENALICE_HOMEoverrides (Docker/dataunchanged;OPENALICE_HOME="$PWD" pnpm devpins a checkout-local store). Legacy checkout stores get a manual-adoption banner (never auto-moved — multiple worktrees have no canonical answer); packaged Electron auto-relocates its singleuserData/datastore crash-safely on first launch.accounts.jsonis now an AES-256-GCM envelope, keyed by~/.openalice/sealing.key(0600, auto-generated, outside the portabledata/subtree, so backing up / sharingdata/no longer carries the key). Migration 0009 reseals existing plaintext stores at first boot; an unreadable store (data copied without its key) is quarantined loudly, never deleted./utano longer echo plaintext credentials (now masked like GET); env naming unified onOPENALICE_HOME(guardians previously injected a variable nothing read); 8 cwd-relativedata/literals routed throughdataPath()— several were latent Electron bugs (UTA wipe path, restart-flag trigger).OPENALICE_HOMEto a temp dir per worker so specs can never touch the real store; the smoke harness pins the checkout so its restart-flag check keeps testing the path it watches.Test plan
npx tsc --noEmitclean (root + apps/desktop)pnpm test— 1848 passing (new: sealing round-trip / plaintext compat / key-loss quarantine / migration 0009 idempotency / Electron relocation / masked echoes)pnpm test:smoke— full stack boots on the new root, UTA restart-flag chain verified end-to-endaccounts.jsonis a$sealedenvelope, 0600Boundary touch
Trading credentials (at-rest sealing + response masking), migrations (0009), data-root relocation (Electron + guardian bootstrap).
🤖 Generated with Claude Code