Skip to main content
← Back to list
01Issue
FeatureShippedSwamp CLI
Assigneesstack72

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`
02Bog Flow
OPENTRIAGEDIN PROGRESSSHIPPED+ 1 MOREASSIGNED+ 2 MOREREVIEW+ 3 MOREPR_MERGED+ 1 MORENOTIFICATION_SKIPPED

Shipped

6/19/2026, 4:47:03 PM

Click a lifecycle step above to view its details.

03Sludge Pulse
stack72 assigned stack726/19/2026, 3:02:12 PM
stack72 linked parent of #6626/19/2026, 3:05:53 PM

Sign in to post a ripple.