Skip to content

feat(cli): address team vaults as <project> <env> - #109

Merged
ralyodio merged 1 commit into
masterfrom
worktree-teams-project-env
Jul 30, 2026
Merged

feat(cli): address team vaults as <project> <env>#109
ralyodio merged 1 commit into
masterfrom
worktree-teams-project-env

Conversation

@ralyodio

Copy link
Copy Markdown
Contributor

What

logicsrc teams push|pull|grant took a single <vault> positional. They now take <project> <env>:

logicsrc teams push  acme web prod --env .env
logicsrc teams grant acme web prod teammate@example.com
logicsrc teams pull  acme web prod --env .env

The two halves are joined into the project/env vault name. CLI-onlyvaultName() / splitVaultName() in packages/cli/src/teams.ts are the only code that knows about the convention, and the server still stores a single opaque vault name. No migration, no API change.

Details

  • Both halves reject /, so the join is unambiguous and splitVaultName is a true inverse of vaultName.
  • teams vaults breaks the stored name back into project / env columns, falling back to the raw name (env shown as ) for vaults that predate the convention.
  • Legacy single-word vaults are no longer addressable, since their names contain no slash. resolveVaultId() now lists the team's actual vault names in the not-found error rather than just saying "not found" — deliberately not a silent fallback to a same-named legacy vault, because in a secrets tool that would mean writing a push somewhere the caller didn't ask for.

Testing

packages/cli/src/teams.test.ts — 8 cases covering the join, project/env distinctness, slash and blank rejection, round-tripping, and null on ambiguous or legacy names.

Full workspace suite green (npm test, all 19 workspaces, fail 0). Note commandboard-web's test fails on a tree where only build:cli has run — it needs commandboard-api/dist, which requires plugin-agentmail, plugin-c0mpute and plugin-sh1pt to be built first. Pre-existing build-ordering, unrelated to this change; it passes once the full chain is built.

push/pull carry two different "env"s — the <env> positional (environment name) and --env (local .env path). Verified against commander that the positional and the option bind separately:

{"slug":"acme","project":"web","env":"prod","optionEnv":".env.local"}
{"slug":"acme","project":"web","env":"prod","optionEnv":".env"}

Internally the positional is bound as envName to keep it distinct from options.env.

Docs

docs/credential-sharing.md updated to the three-positional form.

🤖 Generated with Claude Code

`teams push|pull|grant` took a single `<vault>` name, so a team holding
more than one project had to encode both halves by hand and hope
everyone spelled it the same way. They now take `<project> <env>` and
join them into the `project/env` vault name.

The split lives entirely in the CLI — vaultName()/splitVaultName() are
the only things that know about it, and the server still stores one
opaque vault name — so there's no migration. Both halves reject a "/"
so the join stays unambiguous and the split is a true inverse.

`teams vaults` now breaks the name back into project/env columns,
falling back to the raw name for vaults created before the convention.
Those legacy vaults are no longer addressable (their names don't
contain a slash), so resolveVaultId() lists what the team actually has
instead of just saying "not found" — better than silently retargeting a
push, which in a secrets tool would write to the wrong vault.

Note push/pull carry two different "env"s: the `<env>` positional is
the environment half of the address, `--env` is the local .env path.
Verified commander keeps them separate.

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

Copy link
Copy Markdown

vu1nz Security Review

0 finding(s) in PR #?

No security issues found.

@ralyodio
ralyodio marked this pull request as ready for review July 30, 2026 18:39
@ralyodio
ralyodio merged commit ca182bc into master Jul 30, 2026
5 checks passed
@ralyodio
ralyodio deleted the worktree-teams-project-env branch July 30, 2026 18:39
ralyodio added a commit that referenced this pull request Jul 30, 2026
The "Connect the CLI" card handed out:

    LOGICSRC_API=https://app.logicsrc.com logicsrc login
    logicsrc teams push <team> prod --env .env
    logicsrc teams pull <team> prod --env .env

Two things are wrong with that, and both survived a release.

Since #109 addressed vaults as <team> <project> <env>, push and pull take
three positionals. The hint passes two, so pasting it exits with a missing-
argument error -- the card is not merely stale, it is broken.

The LOGICSRC_API prefix sets the variable to the value the CLI already
defaults to (DEFAULT_API_URL, #107), so on the hosted app it does nothing
while reading like a required step. It is now emitted only when the origin
is not the default, which is the case it exists for: self-hosting.

`--env .env` is dropped for the same reason -- it restates the option's own
default, and sitting next to the new <env> positional it made one flag and
one argument look like the same thing.

Same stale two-argument form fixed in the post-install hint (install.sh) and
the accept-invite message, and in the empty-vault-list prompt on the card.

CLI_HINT moves to src/lib/cli-hint.mjs so a test can assert on the rendered
commands without standing up express and the database, matching how the
other lib-level views are covered. The tests pin the argument count rather
than the prose: restyling the card stays free, dropping an argument does not.

apps/pwa: 13/13 pass.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
ralyodio added a commit that referenced this pull request Jul 30, 2026
…ashes (#112)

#109 addressed vaults as <project>/<env> and shipped broken: every push
failed with

    Vault name must be lowercase letters, numbers, and dashes.

The vault-create endpoint slugifies through /^[a-z0-9][a-z0-9-]{0,62}$/
(apps/pwa/src/routes/credshare.mjs), so a "/" join is refused outright.
Nothing in the CLI ever saw it, because the tests exercised vaultName and
splitVaultName in isolation and never made a request — the one assumption
that mattered, that the server takes an arbitrary vault name, was the one
left unverified.

Switches the separator to "--", which is inside the allowed character set
and still splits unambiguously since neither half may contain one. A
single dash would not: "a-b" + "c" and "a" + "b-c" would collide.

Also validates the joined name against the server's own regex before the
request, so a bad name fails locally with a useful message rather than a
422 after the .env has been read.

The tests now assert the produced name matches that regex, so the
separator cannot drift back out of the allowed set without failing.

Verified end to end against app.logicsrc.com: push, then pull into a
scratch file and diff — keys and values both round-trip losslessly. Then
49 repos pushed under the profullstack team; server reports 49 vaults,
169 secrets, 0 failures.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
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.

1 participant