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

Relationships

↑ child of #662

#680 serve-auth: declarative grants file with startup reconciliation

Opened by stack72 · 6/18/2026

Parent

Sub-issue of #662 (serve authentication & authorization). Layer 2.

Summary

Add support for a declarative grants file — a YAML file checked into the repo that defines authorization grants, reconciled at `swamp serve` startup. This gives infra teams the ability to manage access policy through git rather than imperative CLI commands.

Grants from the file are tagged `source: file` so the reconciler owns only its own records and never touches imperatively-created grants (`source: method`) or config-sourced admin grants (`source: config`).

File format

Location: a `grants.yaml` file (or configurable path) in the repo, e.g. at the repo root or under a config directory.

```yaml grants:

  • subject: idp-group:platform-eng effect: allow actions: [run] resource: workflow:@acme/*

  • subject: idp-group:platform-eng effect: allow actions: [run] resource: workflow:@acme/* condition: 'tags.env == "staging"'

  • subject: idp-group:developers effect: deny actions: [read] resource: data:@acme/pii

  • subject: user:adam effect: allow actions: [admin] resource: access:* ```

The schema should reserve room for a `packs:` key at the top level for phase 2 rule packs, even though it's not implemented yet:

```yaml

Phase 2 — not implemented, but the schema should accept and ignore this key

packs:

  • @acme/soc2-baseline

grants:

  • ... ```

Reconciliation semantics

On `swamp serve` startup (and optionally via a CLI command), the reconciler:

  1. Reads the grants file and parses each entry
  2. Loads all existing grants with `source: file` from the data store via `DataQueryService.query()`
  3. Diffs file entries against stored grants:
    • In file but not in store → create (via Grant model `create` method with `source: file`)
    • In file and in store with matching fields → no-op
    • In file but stored grant has different fields → revoke old, create new (grants are immutable once created — field changes are a new grant)
    • In store with `source: file` but not in file → revoke (the file no longer declares it)
  4. Never touches grants with other sources — `source: method`, `source: config`, or `source: extension:*` are invisible to the reconciler

Identity matching

Grants in the file don't have IDs — the file declares the desired state, not specific instances. Match file entries to stored grants by the tuple `(subject, effect, actions, resource, condition)`. If all fields match, the grant is unchanged. If any field differs, it's treated as a removal + creation.

Validation

Each grant entry is validated at reconciliation time:

  • Subject, resource selector, and actions are parsed through the existing value object parsers
  • CEL conditions are validated via `validateGrantCondition()` from #670
  • Invalid entries fail the reconciliation with a clear error (which entry, which field, what's wrong)
  • The server should refuse to start if the grants file has validation errors — bad policy is not a warning

Where it runs

At serve startup

In `src/cli/commands/serve.ts`, after `requireInitializedRepoUnlocked()` and model registry loading, before the server starts accepting connections. This ensures the policy snapshot is current before any authorization decisions.

Optionally via CLI

A `swamp access reconcile` command (or similar) that runs the same reconciliation logic outside of serve, for testing and debugging the grants file without starting the server.

Scope

  • Grants file parser and schema (YAML, with `packs:` key reserved)
  • Reconciliation service in `src/domain/access/` — pure domain logic, testable in isolation
  • Wire reconciliation into `swamp serve` startup
  • Optionally add a `swamp access reconcile` CLI command
  • Tests: file parsing, reconciliation (create/no-op/revoke), validation errors, `source: file` scoping (doesn't touch other sources)

Out of scope

  • `packs:` key implementation — phase 2 (#662)
  • Admin materialization from config — layer 4
  • Serve enforcement — layer 5

References

  • Grant model: `src/domain/models/access/grant_model.ts`
  • Grant source values: `src/domain/access/grant_source.ts` (`file` is already a valid source)
  • CEL validation: `src/infrastructure/cel/grant_condition_environment.ts`
  • Serve startup: `src/cli/commands/serve.ts` (lines 122-191)
  • Extension reconciliation pattern: `src/libswamp/extensions/reconcile_from_disk_service.ts`
  • Grant query pattern: `queryGrants()` in `src/cli/commands/access_grant.ts`
  • Access CLI (#678): `src/cli/commands/access_grant.ts`
02Bog Flow
OPENTRIAGEDIN PROGRESSCLOSED+ 1 MOREASSIGNED+ 5 MOREREVIEW+ 1 MOREIMPLEMENTATION

Closed

6/18/2026, 5:50:45 PM

No activity in this phase yet.

03Sludge Pulse
stack72 assigned stack726/18/2026, 5:06:49 PM
stack72 linked parent of #6626/19/2026, 3:06:05 PM
Editable. Press Enter to edit.

stack72 commented 6/18/2026, 5:50:52 PM

We reframed this work and closed it

Sign in to post a ripple.