Skip to content

Latest commit

 

History

History
130 lines (110 loc) · 10.4 KB

data-locations.md

File metadata and controls

130 lines (110 loc) · 10.4 KB

Data locations

Application code lives in this repository. Runtime user data never does.

Everything the application writes at runtime lives under the Windows per-user application-data directory:

%APPDATA%\Local-Agent\

which on a typical machine is:

C:\Users\<you>\AppData\Roaming\Local-Agent\

src/main/paths.ts (resolveUserDataPaths) resolves this as join(app.getPath('appData'), APP_DATA_DIR_NAME) — the platform's per-user application-data root, joined with the constant folder name — rather than Electron's app.getPath('userData'), whose folder name instead follows app.name. Deriving it explicitly means the folder is always exactly Local-Agent, regardless of how app.name resolves. Nothing here is ever a user-supplied or renderer-supplied path.

Current state. Milestone 3 implements settings.json: path resolution, defaults on first launch, strict validation, fail-safe fallback on a corrupt or unreadable file, and atomic writes. Milestone 4 implements logs\audit\: an append-only, redacting, UTC-daily-rotating JSONL writer. Milestone 5 implements permissions\policy.json loading: the same fail-safe pattern as settings, plus a pure decision engine and executor gate that consult it. Milestone 6 implements state\emergency.json: the same fail-safe pattern again, with one deliberate difference from the other two — a missing file and a corrupt one resolve to different states (disengaged vs. engaged), not the same safe default — plus atomic engage and reset writes. Milestone 7 implements secrets\secrets.enc: encrypted with the synchronous safeStorage API (Windows DPAPI), never a plaintext fallback. Phase 2 Milestone 8 implements memory\: one file per scope, each written with the same fail-safe load and atomic write pattern, plus the session scope which is never written at all. Its loader distinguishes a missing file from a corrupt one internally, the same way state\emergency.json's does, but — unlike the emergency state — both collapse to the identical safe answer for every caller (no key present), since there is no "fails engaged" equivalent for a credential store. Milestone 7 also wires settings.json and secrets.enc to real IPC channels for the first time: settings:get/settings:update and secrets:status/secrets:write/secrets:clear, all through the unmodified Milestone 5 permission pipeline. permissions\policy.json and state\emergency.json are still read fresh on every one of those calls but still have no writer or channel of their own reachable from the running application. No directory or file is created merely by reading; a directory is created lazily, only on the first write.


Layout

%APPDATA%\Local-Agent\
├── settings.json                    non-secret settings
├── permissions\
│   └── policy.json                  permission policy
├── agents/
│   └── profiles.json                agent profiles (no credential, ever)
├── workflows\
│   └── workflows.json               workflow definitions (manual trigger only)
├── secrets\
│   └── secrets.enc                  encrypted credentials
├── logs\
│   └── audit\
│       └── audit-YYYY-MM-DD.jsonl   append-only audit trail, one file per UTC day
├── state\
│   └── emergency.json               emergency-stop state
└── memory\
    ├── personal.json                personal memory (never a credential)
    └── projects\
        └── <key>.json               one file per approved project
Path Contains Notes
settings.json Assistant name, user name, language, provider selection, hasApiKey flag [implemented, M3, wired to IPC in M7] Never a credential. Strict schema; unknown keys rejected; written atomically; hasApiKey reconciled against secrets.enc on every read and write.
permissions\policy.json Permission rules [implemented, M5] Human-readable and human-editable. Cannot widen the model beyond the code-enforced floor, even if hand-edited to try.
agents\profiles.json Agent profiles [implemented, P2 M7] Human-readable and human-editable. Holds user profiles only — built-ins are read from reviewed source on every load and are never written here, so a hand edit cannot redefine one. Cannot grant a permission: its decision vocabulary has no allow. Declares no field capable of holding a credential.
workflows\workflows.json Workflow definitions [implemented, P2 M9] Human-readable and human-editable. Cannot grant a permission, cannot widen the agent profile it selects, and cannot start itself: its trigger enum has one member, so a hand edit claiming a schedule fails validation and the whole document is discarded. Declares no field capable of holding a credential.
secrets\secrets.enc API keys [implemented, M7] Encrypted with the synchronous safeStorage API (Windows DPAPI). Never leaves the main process in plaintext; presence, not content, is all any IPC channel reports.
logs\audit\ One JSON object per line [implemented, M4] Append-only writer, called for real since M7's IPC channels. Records denials and rejected confirmations exactly like a success.
state\emergency.json Emergency-stop state [implemented, M6] Missing file on first launch means disengaged. Malformed or unreadable existing file means engaged. Written atomically; not called from the running app yet.
memory\personal.json Personal memory records [implemented, P2 M8] Human-readable and human-editable. Plain JSON, deliberately — but it holds notes about a person, so see docs/security-model.md limitations 0.5 and 0.6. Declares no field capable of holding a credential, and credential-shaped values are screened at the write boundary on a best-effort basis.
memory\projects\<key>.json One approved project's memory records [implemented, P2 M8] The file name is a truncated SHA-256 of the project's canonical root path, never the path itself: a directory listing of %APPDATA% should not disclose which projects someone has opened. Reading or writing one requires that project to be approved in the current session.
(none — Windows automation) — [implemented, P2 M10] Windows automation adds no file here. Its tool registry is fixed in reviewed source (shared/automation/registry.ts), not user-editable configuration, so there is nothing for it to persist. See docs/phase-2-automation.md.

Session memory has no entry above, and that is the point. The third memory scope is held in the main process for the lifetime of one run of the application and is never written anywhere. Closing Local Agent ends it; there is no file to find afterwards, and no code path in src/main/memory-store.ts that could create one.

Why they are separate

Keeping these apart means no single file mixes concerns, and a mistake in one area cannot leak into another. Settings can be shared or inspected without exposing credentials. The audit log can be read without touching the policy that produced it. Backing up settings does not back up secrets that would be useless on another machine anyway.

What is deliberately not here

  • Nothing under %PROGRAMDATA% or any machine-wide location. Local Agent is per-user.
  • No registry keys.
  • No temporary files outside the application-data directory.
  • No data in the repository working tree.

For contributors and agents

Do not create runtime user data inside the repository. .gitignore covers the common accidents (/data/, /user-data/, /logs/, /secrets/, /credentials/, .env, *.db), but the rule is the point, not the safety net.

Those directory rules are anchored to the repository root on purpose. An unanchored secrets/ would also match src/main/secrets/, a real module of the application, and would silently drop it from version control. Key files (*.key, *.pem, *.pfx, *.p12) stay unanchored and are ignored wherever they appear.

If you need a scratch file while developing, put it outside the repository or in a git-ignored path, and never put a real credential in it.