Relationships
↑ child of #662#678 serve-auth: swamp access CLI commands
Opened by stack72 · 6/18/2026· Shipped 6/18/2026
Parent
Sub-issue of #662 (serve authentication & authorization). Layer 2.
Summary
Add the `swamp access` CLI command namespace for managing authorization grants and groups. This gives admins the ability to create, list, and revoke grants, manage local groups, and explain access decisions — all operating on the built-in models from #667.
Commands to implement
Grant management
`swamp access grant create` — create a new grant ```bash swamp access grant create \ --subject idp-group:platform-eng \ --allow run \ --on 'workflow:@acme/*'
With a CEL condition:
swamp access grant create \ --subject idp-group:platform-eng \ --allow run \ --on 'workflow:@acme/*' \ --when 'tags.env == "staging"' ```
Arguments:
- `--subject ` — grant subject (`user:adam`, `group:release-managers`, `idp-group:platform-eng`)
- `--allow ` or `--deny ` — effect + actions (comma-separated: `run`, `read`, `write`, `admin`)
- `--on ` — resource selector (`workflow:@acme/`, `model:@acme/deploy`, `data:@acme/reports-`, `access:*`)
- `--when ` — optional CEL condition (validated at creation via #670)
The command creates a named grant instance (auto-generated name) via the Grant model's `create` method with `source: method`.
`swamp access grant list` — list grants ```bash swamp access grant list swamp access grant list --subject idp-group:platform-eng swamp access grant list --on 'workflow:@acme/*' ```
Filterable by subject and/or resource. Shows all active grants with their ID, subject, effect, actions, resource, condition, and source.
`swamp access grant revoke ` — revoke a grant ```bash swamp access grant revoke 7f3a... ```
Calls the Grant model's `revoke` method. Takes effect immediately.
Group management
`swamp access group create ` — create a local group ```bash swamp access group create release-managers ```
`swamp access group add-member ` — add a principal to a group ```bash swamp access group add-member release-managers user:adam ```
`swamp access group remove-member ` — remove a principal from a group ```bash swamp access group remove-member release-managers user:adam ```
`swamp access group list` — list all groups
`swamp access group members ` — list members of a group
Access check (admin explain mode)
`swamp access check` — explain whether a subject can perform an action ```bash swamp access check --subject user:adam --action run --on workflow:@acme/deploy → ALLOW via grant 7f3a… (idp-group:platform-eng → run → workflow:@acme/*) ```
This uses the `AccessDecisionService.explain()` from #672. The admin specifies the subject, action, and resource explicitly. The command resolves group membership from the snapshot and shows all matching grants with their effect and provenance.
Note: `--subject` takes a subject string, but the AccessDecisionService needs an `AccessPrincipal` with collectives. For phase 1, the check command accepts `--collectives` as an optional flag to simulate IdP group membership. In phase 2 (after OAuth), it can look up the principal's actual claims.
Implementation pattern
Follow the existing CLI patterns:
Command hierarchy
``` swamp access (parent, groupCommandAction) ├── grant (parent, groupCommandAction) │ ├── create (leaf) │ ├── list (leaf) │ └── revoke (leaf) ├── group (parent, groupCommandAction) │ ├── create (leaf) │ ├── add-member (leaf) │ ├── remove-member (leaf) │ ├── list (leaf) │ └── members (leaf) └── check (leaf) ```
File structure
- `src/cli/commands/access.ts` — parent command, registers subcommands
- `src/cli/commands/access_grant.ts` — grant subcommands (create, list, revoke)
- `src/cli/commands/access_group.ts` — group subcommands
- `src/cli/commands/access_check.ts` — check command
- `src/presentation/output/access_grant.ts` — grant renderers (log + json)
- `src/presentation/output/access_group.ts` — group renderers (log + json)
- `src/presentation/output/access_check.ts` — check renderer (log + json)
- Tests for each
Output modes
Every command must support both `log` (human-readable) and `json` (structured) output modes, following the renderer pattern in `src/presentation/renderers/`. Use `createContext(options, ["access", "grant"])` for logger categories.
Dependencies
Grant and group operations go through model method execution — use the same `modelMethodRun` / libswamp patterns the existing CLI uses. The `access check` command needs to build a `PolicySnapshot` and `GrantBasedAccessDecisionService` to evaluate. Follow how the serve layer builds these (see `PolicySnapshotLoader` from #672).
Scope
- All commands listed above with both output modes
- Register `accessCommand` in the root CLI (`src/cli/mod.ts`)
- Tests verifying command registration, option shapes
- No serve wiring — these operate on the local repo
Out of scope
- `can-i` command — layer 5 (needs authentication to know caller identity)
- Declarative grants file — separate layer 2 issue
- Admin materialization — layer 4
- Serve enforcement wiring — layer 5
References
- CLI command pattern: `src/cli/commands/data.ts` (parent), `src/cli/commands/data_get.ts` (leaf)
- Group action for parents: `src/cli/group_action.ts`
- Renderer pattern: `src/presentation/renderers/data_get.ts`
- Root CLI registration: `src/cli/mod.ts` (line ~1239)
- CLI test pattern: `src/cli/commands/data_get_test.ts`
- Grant model: `src/domain/models/access/grant_model.ts`
- Group model: `src/domain/models/access/group_model.ts`
- AccessDecisionService: `src/domain/access/grant_based_access_decision_service.ts`
- PolicySnapshotLoader: `src/domain/access/policy_snapshot_loader.ts`
- Access value objects: `src/domain/access/mod.ts`
Shipped
Click a lifecycle step above to view its details.