Skip to content

docs: RFC 0001 for declarative management of platform resources#1751

Open
rohilsurana wants to merge 1 commit into
mainfrom
docs/reconcile-rfc
Open

docs: RFC 0001 for declarative management of platform resources#1751
rohilsurana wants to merge 1 commit into
mainfrom
docs/reconcile-rfc

Conversation

@rohilsurana

Copy link
Copy Markdown
Member

Proposes a declarative way to manage Frontier's platform configuration: desired-state YAML files, the frontier reconcile and frontier export commands, and per-kind behavior rules.

What it covers

  • The problem with the three current flows: config-driven superusers, boot-time resource YAML, and hand-run API changes.
  • The file format, the commands, and the reconcile framework.
  • Rules every kind must follow, so future kinds start from the same contract: server-managed objects stay invisible, identities never change, listed fields are managed, every kind declares what a missing entry means (fail the plan / removal / reset to default) and what deletion means, adds run before removes, and every kind exports deterministically with a clean round trip.
  • The first three kinds against those rules: PlatformUser (shipped in v0.108.0), and Permission and Role (proposed here).
  • Two boot behavior changes: superuser promotion removal (shipped) and boot no longer updating existing roles (proposed), with the upgrade trade-off stated.
  • Decisions with reasons, alternatives considered, and known drawbacks.

Status

PlatformUser shipped and set the pattern. #1731 (export command) and #1737 (Permission and Role kinds) are draft implementations of this proposal; they change with this RFC's review and merge after it.

@vercel

vercel Bot commented Jul 13, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
frontier Ready Ready Preview, Comment Jul 13, 2026 3:29am

@coderabbitai

coderabbitai Bot commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Summary by CodeRabbit

  • Documentation
    • Added an RFC describing declarative management of platform resources through YAML desired-state files.
    • Documented frontier reconcile, including validation, ordered processing, reporting, applying changes, and dry-run support.
    • Documented frontier export for exporting live resource state.
    • Defined resource identity, validation, dependency ordering, field management, and explicit deletion behavior.
    • Added guidance for managing platform users, permissions, and roles, including predefined role constraints.

Walkthrough

The RFC documents a declarative YAML workflow for reconciling and exporting Frontier resources, defines shared kind behavior and rules for PlatformUser, Permission, and Role, and specifies supporting server changes, decisions, trade-offs, and future work.

Changes

Declarative reconciliation design

Layer / File(s) Summary
Workflow goals and command framework
docs/rfcs/0001-declarative-reconcile.md
Defines the desired-state format, reconcile/export commands, validation and API behavior, and the framework interface for registering resource kinds.
Kind contracts and reconciliation rules
docs/rfcs/0001-declarative-reconcile.md
Specifies universal reconciliation rules and detailed behavior for PlatformUser, Permission, and Role.
Server support and design decisions
docs/rfcs/0001-declarative-reconcile.md
Documents required server boot changes, decisions, alternatives, trade-offs, future work, and references.

Estimated code review effort: 2 (Simple) | ~15 minutes

🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5


ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: d8fb691c-6b4f-4ac9-9f5e-4c8db0d04f33

📥 Commits

Reviewing files that changed from the base of the PR and between cd6b070 and a8506ce.

📒 Files selected for processing (1)
  • docs/rfcs/0001-declarative-reconcile.md

Comment on lines +88 to +90
- `frontier reconcile -f <file> [--dry-run] --host <host> -H "Authorization:Basic <token>"`
parses and validates the whole file first, then handles each document in file order. It
prints one report per kind: `no changes`, a plan (dry run), or what was applied.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Do not claim whole-file validation before apply without a preflight phase.

The RFC promises that the entire file is parsed and validated before any change applies. However, reconcile.Run dispatches documents sequentially and each reconciler applies immediately; a later malformed, unknown, or invalid document can therefore run after earlier documents have already changed the server. Either add a validation-only phase or document partial application as supported behavior.

Also applies to: 136-139

Comment on lines +88 to +95
- `frontier reconcile -f <file> [--dry-run] --host <host> -H "Authorization:Basic <token>"`
parses and validates the whole file first, then handles each document in file order. It
prints one report per kind: `no changes`, a plan (dry run), or what was applied.
- `frontier export <kind>` prints the live state of one kind as a desired-state document on
stdout. The kind argument is case-insensitive and accepts a plural.

Both commands talk to the admin API. They log in with any superuser credential; the
bootstrap service account exists so automation always has one.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift

Avoid exposing bearer credentials in process arguments.

The documented examples pass the authorization token through -H; command-line arguments can be visible through process listings and shell history. Provide an environment-variable, file, stdin, or client-configuration mechanism before treating this as the standard automation workflow.

Also applies to: 306-307

Comment on lines +89 to +90
parses and validates the whole file first, then handles each document in file order. It
prints one report per kind: `no changes`, a plan (dry run), or what was applied.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Implement dependency ordering or require validated file ordering.

The RFC says documents are processed in file order but also guarantees that permissions apply before roles. The supplied reconcile.Run implementation has no dependency sorting, so a Role document appearing before its Permission can fail. Topologically order registered kinds, or explicitly require and validate dependency order.

Also applies to: 140-145

Comment on lines +99 to +105
Each kind implements a small interface in `internal/reconcile`:

- `Reconcile(spec, dryRun)`: read the current state through the API, diff it against the
spec, report the plan, and apply it unless this is a dry run.
- `Export()` (optional): return the current state as a spec value.

New kinds register in one map in the CLI. The behavior rules every kind must follow are

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Make the export contract consistent.

Lines 103-104 describe Export() as optional, while rule 9 requires every kind to implement export. The actual Reconciler interface also contains only Kind() and Reconcile(). Define export as a mandatory registered capability, or specify the exact behavior for kinds without export.

Also applies to: 143-145

Comment on lines +272 to +274
7. **Reads and writes use the public API**, not the database. The reconciler is an API
client like any other: every change is validated, audited, and visible to all pods at
once. It also means the CLI works against any reachable Frontier, old or new.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Qualify the “old or new” server compatibility claim.

The current CLI registration supports only PlatformUser, while Permission and Role are proposed additions. A server that predates those APIs cannot support every documented kind. State the minimum compatible server version or define capability negotiation.

@coveralls

Copy link
Copy Markdown

Coverage Report for CI Build 29221775459

Coverage remained the same at 44.876%

Details

  • Coverage remained the same as the base build.
  • Patch coverage: No coverable lines changed in this PR.
  • No coverage regressions found.

Uncovered Changes

No uncovered changes found.

Coverage Regressions

No coverage regressions found.


Coverage Stats

Coverage Status
Relevant Lines: 37644
Covered Lines: 16893
Line Coverage: 44.88%
Coverage Strength: 12.54 hits per line

💛 - Coveralls

@rohilsurana rohilsurana left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verified the narrowing warning against the schema and against a live server (built from #1737, seeded an org, narrowed app_organization_owner via reconcile). One sentence in it is factually wrong; details inline. More comments on the other sections will follow.

directly, outside roles. For example, organization `get` is also granted to the org's
`member` and `owner` relations and to platform superusers. Removing `get` from a role does
not block someone who also holds one of those direct grants. Role-only permissions (like
`rolemanage`, `policymanage`, and all custom permissions) always flip cleanly. Before

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This sentence is wrong for rolemanage and policymanage. Both have direct grants in base_schema.zed, exactly like the get example above:

permission rolemanage   = platform->superuser + granted->app_organization_administer + granted->app_organization_rolemanage + owner
permission policymanage = platform->superuser + granted->app_organization_administer + granted->app_organization_policymanage + owner

An org owner keeps both no matter what the role says. In fact almost every predefined org permission carries + owner. Only custom permissions flip cleanly, because they are granted purely through granted-> role paths.

I tested this on a live server built from #1737. Two users held app_organization_owner: one with the owner relation (the normal case — every membership path pairs the role with the relation via orgRoleToRelation), one with only a role policy. After narrowing the role's permissions to [get, update, policymanage] via reconcile:

Check owner relation + role role only
rolemanage still allowed denied
delete still allowed denied

So the role mechanism works instantly for role-path access, but the direct owner grant bypasses it for every real owner.

Suggested fix for the paragraph: name only custom permissions as the clean case, and note that narrowing a predefined role today does not restrict org owners at all, because ownership is granted through the owner relation directly in the schema. Worth adding the forward-looking point too: once ownership moves fully to roles and the relation-based grants (+ owner, + member) come out of the base schema, reconcile will be able to properly control access for predefined roles as well. That migration is out of scope here, but naming it explains why the limitation exists and where it goes.

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.

2 participants