feat: project-scoped secrets (MVP)#11
Open
tashian wants to merge 3 commits into
Open
Conversation
…oping Adds the daemon half of project-scoped secrets (issue #7 MVP). - Secret/SecretMetadata gain `scope` ("global" | "project") and `roots: [String]`. Backward-compatible decode via custom Secret decoder defaults (`scope="global"`, `roots=[]`); no envelope-version bump. - New `PeerCwd.resolve(peerPID:proc:)` queries the kernel via `proc_pidinfo(PROC_PIDVNODEPATHINFO)` for the connecting peer's cwd. Closure-injected `proc` lets the test suite fake the syscall layer, mirroring `PeerSession.resolveDurableSessionID`'s pattern. - `Vault.add` validates scope+roots: scope must be in {global, project}; project requires non-empty, absolute, normalized roots, capped at 16. - `Vault.get` and `Vault.list` consult `peerCwd` and filter project-scoped secrets accordingly. New `VaultError.secretOutOfScope(name:roots:)` surfaces as wire error code -32010 with structured `{name, roots}` data so the CLI can render an actionable hint. - `SocketServer` resolves cwd per-RPC (not at accept) so a long-lived connection sees each `cd`. peerPID is captured once at accept.
CLI half of project-scoped secrets (issue #7 MVP). - `tsm add --here` resolves cwd via os.Getwd + filepath.EvalSymlinks and binds the new secret to that path. `--project /abs/path` (repeatable) lets the user bind to additional roots. Both flags imply scope=project and dedupe roots before sending. Relative paths are rejected client-side. - `tsm list` defaults to the cwd-filtered view. `--all` opts out by passing `include_all=true` to the daemon. List output gains a "scope: project (root1, root2)" line for project-scoped rows. - `tsm get` maps the new daemon error code (-32010, secret_out_of_scope) to a hint that names the secret, lists the bound root(s), and points the user at `tsm list --all`. - `internal/jsonrpc` exposes `CodeSecretOutOfScope = -32010` for callers. - `runAdd` and `runList` were refactored into `runAddWith` / `runListWith` so tests can inject mock callers and stub Getwd / EvalSymlinks; the previously untested commands now have coverage.
Adds a "Project-scoped secrets" section to credential-usage SKILL.md between §1 and §2: - Default `tsm list` is cwd-filtered; trust it. - Out-of-scope secret should not trigger a `cd` workaround — explain to the user and offer `tsm list --all` instead. - How to save a project-bound secret (`pbpaste | tsm add --here ...`). - Project scope is a UX boundary, not a security one. The local design doc at docs/plans/2026-05-04-project-scoped-tokens-design.md captures the eight design decisions and the threat-model paragraph; per .gitignore convention, plans live locally and are not committed.
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
scope("global" | "project") androots: []stringto Secret. Backward-compatible decode; no envelope bump.proc_pidinfo(PROC_PIDVNODEPATHINFO)(PeerCwd.resolve) — the CLI never claims its own cwd.tsm add --here/tsm add --project <path>(repeatable) for project binding.tsm listdefaults to in-scope filter;--allreveals everything.tsm getreturnssecret_out_of_scope(RPC code -32010) when the calling cwd is outside any bound root, with a CLI hint.docs/plans/is gitignored).Deferred to follow-ups:
tsm runauto-inject,tsm edit --add-root/--remove-root, recent-projects cache,tsm projectnoun.Closes #7 (MVP — see PR body for deferred items)
Test plan
cd tsmd && swift testpasses (incl. PeerCwdTests, scope validation, cwd resolution)go test ./...passes (incl. cmd/add_test.go --here, cmd/list_test.go --all, cmd/get_test.go out-of-scope hint)tsm add --here --name foo --no-inputfrom/tmp/projthencd /tmp/other && tsm get fooreturns the hint