Skip to content

feat(m4): session-key store via @napi-rs/keyring#311

Merged
0xDevNinja merged 3 commits intomilestone/M4-indexer-api-gateway-sdk-alphafrom
feat/M4-session-keys
Apr 29, 2026
Merged

feat(m4): session-key store via @napi-rs/keyring#311
0xDevNinja merged 3 commits intomilestone/M4-indexer-api-gateway-sdk-alphafrom
feat/M4-session-keys

Conversation

@0xDevNinja
Copy link
Copy Markdown
Owner

Summary

  • @titular/acp-sdk/session-keys adds SessionKeyStore wrapping @napi-rs/keyring for OS-keychain-backed secret storage (macOS Keychain, Windows Credential Manager, libsecret).
  • Typed API: set/get/delete per (service, account) pair; service name namespaced (default titular-acp).
  • inMemorySessionKeyStore() fallback for --non-interactive CI flows and tests where no keychain is reachable.
  • New error code keyring_unavailable so consumers can distinguish missing-native-module vs missing-account.
  • @napi-rs/keyring is in optionalDependencies: native module fails on some build envs; missing dep degrades to in-memory with a console.warn.

Closes #98.

Test plan

  • vitest unit tests cover keyring-present path + in-memory fallback
  • typecheck + build clean
  • gateway-go / indexer-go skipping per paths-filter (no Go changes)

Follow-up

Wiring SessionKeyStore into packages/acp-cli/src/config.ts is deferred — currently CLI uses 0o600 plaintext at ~/.config/titular/config.json. Will land as a separate PR after this merges to avoid touching multiple packages in one PR.

Adds `SessionKeyStore` wrapping @napi-rs/keyring (OS keychain — macOS
Keychain, Windows Credential Manager, libsecret on Linux) for secure
storage of EVM private keys / SIWE JWTs / session secrets.

Public API on `@titular/acp-sdk/session-keys`:
- `SessionKeyStore({ service })` — typed wrapper with `set/get/delete`
- `inMemorySessionKeyStore()` — fallback for `--non-interactive` CI
  flows and tests where no OS keychain is reachable
- New error code `keyring_unavailable` so consumers can distinguish

@napi-rs/keyring is `optionalDependencies`: native module fails on some
build envs; missing dep degrades to in-memory with a console.warn.

Service name is namespaced (default `titular-acp`) so multiple deploys
on the same host don't collide on entries.
@0xDevNinja 0xDevNinja added milestone:M4 M4 milestone PR status:needs-review Awaiting review labels Apr 29, 2026
CI runs `pnpm install --frozen-lockfile`, which fails when package.json
declares an optional dep that isn't in the lockfile. Refreshes the
lockfile to register `@napi-rs/keyring@^1.1.0` under the acp-sdk-ts
importer's `optionalDependencies` block.
@0xDevNinja
Copy link
Copy Markdown
Owner Author

LGTM — APPROVE.

Verified:

  • Optional dep dance: dynamic import(specifier) with /* @vite-ignore */ keeps native module out of static graph; optionalDependencies lets npm i succeed when prebuilt unavailable; try/catch handles missing module → keyring_unavailable error code.
  • Fallback explicit: console.warn names package + states "Secrets will NOT persist across process restarts."
  • API symmetric: set/get/delete per (service, account). Service namespacing documented.
  • 9 files / 149 tests pass; 96.77% coverage on session-keys.ts.
  • Subpath export ./session-keys doesn't pull viem.
  • OWASP: secrets never in error messages (only in cause), test asserts no hunter2/passphrase/socket/0xdeadbeef in wrapped messages.
  • Lockfile refreshed in fix commit (CI was failing --frozen-lockfile until lockfile registered @napi-rs/keyring).
  • forge test flake (DeployPhase2 race, TS-only PR) cleared on rerun.
  • All 25 CI checks green.

Follow-ups (non-blocking):

  • Linux libsecret-1 install gotcha — add README note for headless/Alpine envs.
  • serviceForChain(chainId) helper to make namespacing harder to misuse.
  • Wire SessionKeyStore into packages/acp-cli/src/config.ts (deferred per PR body).

Squash-merging into milestone/M4-indexer-api-gateway-sdk-alpha.

@0xDevNinja 0xDevNinja mentioned this pull request Apr 29, 2026
7 tasks
Resolves pnpm-lock.yaml conflict caused by sibling PR #312 (examples)
landing first. Re-runs `pnpm install` against the merged package.json
graph so the lockfile carries both the `@napi-rs/keyring` optional dep
(this PR) and the latest examples-branch dep resolutions.
@0xDevNinja 0xDevNinja merged commit 2efc025 into milestone/M4-indexer-api-gateway-sdk-alpha Apr 29, 2026
23 checks passed
@0xDevNinja 0xDevNinja deleted the feat/M4-session-keys branch April 29, 2026 20:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

milestone:M4 M4 milestone PR status:needs-review Awaiting review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant