Relationships
↑ child of #662#690 serve-auth: swamp access can-i — user self-service permission check
Opened by stack72 · 6/19/2026· Shipped 6/19/2026
Parent
Sub-issue of #662 (serve authentication & authorization). Layer 5.
Dependencies
- ✅ #672 — AccessDecisionService
- ✅ #685 — `mode: token` authentication (principal on connection)
- ✅ #688 — Authorization enforcement at chokepoints
Summary
Users need to answer "what can I do?" without bothering the admin. `swamp access can-i` lets an authenticated user check their own permissions against the server's grants — using the connection's authenticated principal, not a `--subject` flag.
This is the user-facing complement to `swamp access check` (which is admin-only and requires `--subject`).
UX
```bash
Check a specific permission
swamp access can-i --action run --on workflow:@acme/deploy \ --server wss://swamp.acme.internal:9090
→ ✓ ALLOWED via grant 7f3a… (idp-group:platform-eng → run → workflow:@acme/*)
Check something you can't do
swamp access can-i --action admin --on access:* \ --server wss://swamp.acme.internal:9090
→ ✗ DENIED — no matching grants for user:adam admin access:*
List everything you can do (no --action/--on, enumerate all grants)
swamp access can-i --server wss://swamp.acme.internal:9090
→ workflow:@acme/* run ✓ (via idp-group:platform-eng)
data:@acme/dev-* read ✓ (via idp-group:development)
data:@acme/infra-audit-* read ✗ (denied via idp-group:development)
```
What to build
1. New `access.can-i` protocol frame type
Add to `src/serve/protocol.ts`:
```typescript // Request — subject is NOT in the payload (server uses connection principal) interface AccessCanIPayload { action?: string; // optional — if omitted, enumerate all resource?: string; // optional — if omitted, enumerate all }
// Response interface AccessCanIResponse { principal: string; // the authenticated principal (server tells you who you are) decisions: { action: string; resource: string; effect: string; grantId: string; via: string; condition?: string }[]; } ```
When `action` and `resource` are both provided, check that specific permission. When omitted, enumerate all grants that apply to the connection's principal.
2. Server-side handler
Add `handleAccessCanI` to `src/serve/connection.ts`:
- Uses the connection's `principal` parameter directly — no `--subject` parsing
- Does NOT require `admin` on `access:*` — this is a self-service check, any authenticated user can query their own permissions
- When checking a specific action/resource: calls `service.explain()` with the connection principal
- When enumerating: loads all grants from the snapshot, filters to those matching the principal's subjects (user, groups, idp-groups), returns the full set
- Builds `AccessPrincipal` with empty collectives for `mode: token` (same as enforcement)
3. CLI command
`swamp access can-i` with flags:
- `--server ` — required (can-i only makes sense against a server where you have an identity)
- `--token ` — optional (falls back to stored credential)
- `--action ` — optional (if omitted, enumerate all)
- `--on ` — optional (if omitted, enumerate all)
- `--collectives ` — optional (override IdP groups for testing)
No `--subject` flag. No local mode. This command always runs against a server.
4. Renderer
Both log and JSON output modes:
Log mode (specific check): ``` ✓ ALLOWED via grant 7f3a… (idp-group:platform-eng → run → workflow:@acme/*) ```
Log mode (enumeration): ``` workflow:@acme/* run ✓ (via idp-group:platform-eng) data:@acme/dev-* read ✓ (via idp-group:development) data:@acme/infra-audit-* read ✗ (denied via idp-group:development) ```
JSON mode: raw decisions array.
Key design point
`can-i` does NOT require admin privileges. The authorization check on `handleAccessCanI` should only verify the principal is authenticated (not null), not that they have `admin` on `access:*`. Every authenticated user can check their own permissions — that's the whole point.
This is different from `access check` which lets an admin check ANY subject's permissions and therefore requires admin authority.
Scope
- New protocol frame type `access.can-i` (request + response)
- Server-side handler in connection.ts
- `swamp access can-i` CLI command
- Renderer for both output modes
- Register in access command group and connection message dispatcher
- Tests
Out of scope
- Modifying existing `access check` command
- OAuth / collectives population (empty for mode: token)
References
- Access check (admin version): `src/cli/commands/access_check.ts`
- Connection handler + enforcement: `src/serve/connection.ts`
- Protocol types: `src/serve/protocol.ts`
- AccessDecisionService: `src/domain/access/grant_based_access_decision_service.ts`
- PolicySnapshot: `src/domain/access/policy_snapshot.ts`
- Remote run + token resolution: `src/cli/remote_run.ts`
Shipped
Click a lifecycle step above to view its details.