Skip to content

Secret ⇄ service binding (CLI)#59

Merged
jwfing merged 1 commit into
mainfrom
feat/secret-service-binding
Jul 17, 2026
Merged

Secret ⇄ service binding (CLI)#59
jwfing merged 1 commit into
mainfrom
feat/secret-service-binding

Conversation

@jwfing

@jwfing jwfing commented Jul 17, 2026

Copy link
Copy Markdown
Member

CLI surfaces for binding secrets to services (platform PR InsForge/insta-platform#47).

  • insta secrets set <NAME> [value] --service <type/name> — bind a user secret to a branch service (binding requires a branch; defaults to the current one).
  • insta secrets list [--branch] [--json] — now grouped by service (+ branch-level unbound + project-wide), names only.
  • insta secrets tree [--json] — the whole project as project → branch → service → secrets (names only).
  • insta services secrets <type> <name> [--branch] [--json] — a service's bound secret names.
  • All three read commands handle the gated secrets.read 202 approval flow (via rawRequest+handleApproval, like the flat secrets fetch). The .env bundle is unchanged; new views are names-only.

Verification: npm run typecheck && npm run build && npm test (92 tests) pass. Agent-facing docs in the insta-skills PR.

🤖 Generated with Claude Code


Summary by cubic

Add CLI support for binding secrets to branch services and new views to inspect secret bindings across a project. This scopes and audits secrets by service while keeping the .env bundle unchanged.

  • New Features
    • Bind a secret to a branch service: insta secrets set <name> [value] --service <type/name> (implies current branch; respects --branch).
    • Grouped listing: insta secrets list [--branch] [--json] now shows project-wide, per-service, and branch-level unbound names.
    • Full tree view: insta secrets tree [--json] prints project → branch → service → secrets (names only).
    • Service view: insta services secrets <type> <name> [--branch] [--json] lists names bound to a service.
    • Read commands respect the gated secrets.read 202 approval flow.

Written for commit eafb706. Summary will update on new commits.

Review in cubic

… secrets

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@jwfing jwfing left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review — Secret ⇄ service binding (CLI)

Summary: Clean, well-scoped CLI surface for secret↔service binding; the new commands' request/response shapes match the companion platform PR, and I found no blocking issues.

Requirements context

insta-cli has no docs/superpowers/ or docs/specs/ directory, so there is no in-repo spec/plan to check against — assessing against the PR description and the companion platform PR InsForge/insta-platform#47. I cross-checked the CLI's API assumptions against that PR's openapi.yaml, src/server.ts, and src/provisioning/userSecrets.ts (see Functionality below).

Findings

Critical

(none)

Suggestion

  • [software-engineering] No tests were added for the new/changed CLI surface (secretsTree, servicesSecrets, the rewritten secretsList, and the --service branch-defaulting in secretsSet). This is consistent with the repo convention — only pure helpers are unit-tested (test/services.test.ts, run-secrets.test.ts), while command wrappers that call ApiClient.load() are not — so it is not blocking. But secrets.ts:81 (const branch = opts.service ? (opts.branch ?? p.branch) : opts.branch) and the tree-rendering in renderBranch (secrets.ts:36-39) are real logic that could be extracted into pure functions and unit-tested, matching the "pure, unit-tested helpers" pattern in services.ts:12-44. Worth noting the platform side (#47) shipped test/secret-service-binding.test.ts; the CLI side shipped none.
  • [functionality] secretsList (secrets.ts:54-65): when the requested branch has no entry in tree.branches (e.g. a branch with no services and no unbound secrets), the command prints project-wide names and then says nothing at all about the branch. Consider a (no branch-scoped secrets) line for parity with servicesSecrets' empty-state message (secrets/services.ts:146).

Information

  • [functionality] Response shapes verified correct against platform #47:
    • GET /secrets/tree returns SecretTree at the top level → CLI reads res.body as the tree (secrets.ts:47, :60). ✓
    • GET /services/{id}/secrets returns { secrets: [...] } → CLI reads res.body.secrets (services.ts:144). ✓
    • PUT /secrets/{name} now accepts optional service → CLI sends service: opts.service (secrets.ts:82). ✓
    • Server registers /secrets/tree before /secrets/:name, so tree is not captured as a :name param — no client-side concern.
  • [performance] secretsList now fetches the entire project tree (all branches) and filters client-side (secrets.ts:58-61), where it previously hit a branch-scoped endpoint. Platform #47 exposes no branch-scoped tree endpoint, so this is the only option; blast radius is a single CLI invocation — noted only, no action needed.
  • [software-engineering] svc secrets registers --branch <b> (index.ts:121) while every other services subcommand uses --branch <branch>. Cosmetic inconsistency in --help output.
  • [software-engineering] Per the developing-insta-cli skill, command/flag changes should be mirrored in skills/insta/cli-reference.md "in the same change set." The PR body notes those docs live in a separate insta-skills PR — acceptable since that reference is a cross-repo submodule, but please land the two together so the agent-facing docs don't lag the CLI.
  • [security] No security-relevant regressions: values still come from arg/stdin over the authenticated client; the three new read views are names-only and go through the same secrets.read 202 approval gate as the flat fetch; servicesSecrets validates its type arg via assertType and the --service ref is validated server-side; nothing new is logged and no secret values are printed by the new views.

Verdict

approved — zero Critical findings. (Informational; explicit GitHub approval remains a separate human action.) The suggestions above are non-blocking.

@Fermionic-Lyu Fermionic-Lyu left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, Approved. (Relaying John-bot's approved verdict — approved with the maintainer account, since John-bot can't approve its own PR.)

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 3 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="src/commands/secrets.ts">

<violation number="1" location="src/commands/secrets.ts:61">
P2: `insta secrets list --branch <unknown>` silently looks like an empty branch because the missing branch is ignored and only project-wide names are printed. Validating the lookup before either output path would turn typos or stale links into an actionable error and keep the JSON shape stable.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread src/commands/secrets.ts
if (handleApproval(res)) return
for (const name of Object.keys(res.body.secrets)) info(name)
const tree: Tree = res.body
const b = tree.branches.find((x) => x.name === branch)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: insta secrets list --branch <unknown> silently looks like an empty branch because the missing branch is ignored and only project-wide names are printed. Validating the lookup before either output path would turn typos or stale links into an actionable error and keep the JSON shape stable.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/commands/secrets.ts, line 61:

<comment>`insta secrets list --branch <unknown>` silently looks like an empty branch because the missing branch is ignored and only project-wide names are printed. Validating the lookup before either output path would turn typos or stale links into an actionable error and keep the JSON shape stable.</comment>

<file context>
@@ -25,13 +25,43 @@ export async function secrets(opts: { branch?: string; output?: string; print?:
   if (handleApproval(res)) return
-  for (const name of Object.keys(res.body.secrets)) info(name)
+  const tree: Tree = res.body
+  const b = tree.branches.find((x) => x.name === branch)
+  if (opts.json) return printJson({ projectWide: tree.projectWide, branch: b })
+  if (tree.projectWide.length) { info('(project-wide)'); for (const n of tree.projectWide) info(`  ${n}`) }
</file context>
Suggested change
const b = tree.branches.find((x) => x.name === branch)
const b = tree.branches.find((x) => x.name === branch)
if (!b) die(`branch not found: ${branch}`)

@jwfing
jwfing merged commit da9944f into main Jul 17, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants