Reconcile
Frontier can manage parts of its platform configuration from a YAML file instead of
one-off API calls. You write down what should exist, and the frontier reconcile command
makes the server match it. The frontier export command does the reverse: it prints what
the server has, in the same file format.
This gives you three things the API alone does not:
- A reviewable file that says what should exist. Keep it in git and change it via pull requests.
- A plan before every change. A dry run prints every add, update, and delete the file would cause, without applying anything.
- Removal that works. Anything the file no longer wants shows up in the plan and is removed on apply.
The desired-state file
A file holds one or more YAML documents. Each document names the format version, a kind of resource, and a spec:
apiVersion: v1
kind: PlatformUser
spec:
- type: user
ref: alice@example.org
relation: admin
- type: user
ref: bob@example.org
relation: member
- type: serviceuser
ref: 9d776a1c-2f2e-4e56-a6f9-71a25a2eab5f
relation: adminRules that apply to every document:
- A document without
apiVersionis read asv1. An unknown version is rejected. - A document with a missing spec is rejected, because that is usually a typo. To mean an
empty list on purpose, write
spec: []. - The whole file is parsed and checked before anything applies. A malformed later document stops the run before any change is made.
- Documents apply in the order they appear in the file.
- Some kinds depend on others. A role can grant a permission, so a
Permissiondocument must come before aRoledocument in the same file. The wrong order is rejected before anything applies.
The PlatformUser kind
PlatformUser manages who holds platform-wide access: admin (superuser) or member.
An entry is:
| Field | Value |
|---|---|
type | user or serviceuser |
ref | email or id for a user; id for a service user |
relation | admin or member |
The file is the full access list. Anyone listed gets that access. Anyone on the server but not in the file loses it. To add access, add an entry; to remove it, delete the entry. Someone who holds both relations has two entries.
Two safety properties:
- Adds run before removes, so changing someone from
admintomembernever leaves them with no access if a step fails in between. - The bootstrap service user (the automation account from
app.admin.bootstrap) is never touched: the reconciler skips it on the server side and rejects file entries that name it.
Adding a user by an email that does not exist creates that user.
The Permission kind
A permission is an identity: a service/resource namespace plus a verb. There is nothing
else to manage on it, so it is either created or deleted.
apiVersion: v1
kind: Permission
spec:
- namespace: compute/order
name: get
- namespace: compute/order
name: legacy
delete: true- The namespace has two parts,
service/resource. The name is alphanumeric. - Frontier's own permissions (the
appnamespaces) belong to the base schema. The reconciler ignores them and rejects a file entry that names one. - A custom permission on the server that the file does not list fails the plan. Nothing
is deleted just because it is missing; deleting needs
delete: trueon the entry. - Creating a permission also updates the authorization schema, so a new permission is ready to use in roles right away.
The Role kind
Role manages platform-level roles. The role name is the identity and never changes. The
managed fields are the title, the description, the permissions, and the scopes (the
resource types a role can attach to).
apiVersion: v1
kind: Role
spec:
- name: compute_order_manager # custom role: must list its permissions
title: Order Manager
description: Manages compute orders
permissions:
- compute_order_get
- compute_order_update
- name: app_project_viewer # predefined role: override only the fields you list
permissions:
- app_project_get
- resource_aoi_get
- name: old_role
delete: trueThere are two kinds of roles, and they behave differently.
Custom roles are yours to manage in full. A custom role must list at least one
permission. Every custom role on the server must appear in the file, kept or marked
delete: true; one that is missing fails the plan. A role that still has policy bindings
cannot be deleted, and the apply fails with a clear error.
Predefined roles are the ones Frontier ships, like app_organization_owner or
app_project_viewer. They converge to their shipped definitions. A file entry overrides
only the fields it lists; a field you leave out goes back to the default. A predefined
role you do not list at all resets fully. The plan marks these resets, so a hand-made
change to a predefined role shows up as an update back to the default. You cannot delete a
predefined role; the server recreates it at boot.
Because a predefined role resets to its default, removing its entry from the file is a
real change. If an entry adds permissions to app_project_viewer, deleting that entry
takes those permissions away on the next apply.
Permission references accept any form the server knows: the slug (compute_order_get),
service/resource:verb, or service.resource.verb.
Running it
Log in as a superuser. The bootstrap service user exists for exactly this; its client id and secret make a Basic token:
BASIC=$(printf '%s:%s' "$CLIENT_ID" "$CLIENT_SECRET" | base64 | tr -d '\n')Always dry-run first and read the plan:
$ frontier reconcile -f platform-users.yaml --dry-run \
--host <host> -H "Authorization:Basic ${BASIC}"
PlatformUser (planned 2):
- add user alice@example.org as admin
- remove user 5f7b...9c1d (member)Then apply by running the same command without --dry-run. The report shows what was
applied; a run with nothing to do prints PlatformUser: no changes.
The -H flag is an interim way to pass the token: command arguments are visible in
process listings, so automation should mask the token in its logs.
Exporting the current state
frontier export <kind> prints the live state as a desired-state document on stdout:
frontier export platformuser --host <host> -H "Authorization:Basic ${BASIC}" > platform-users.yamlUse it to write the first version of a file from a running server. The output is sorted, so exporting twice gives identical files, and it round-trips: reconciling an export's output always plans no changes. A clean dry run is proof that a file matches its server.
The kind argument is case-insensitive and accepts a plural, so platformuser and
PlatformUsers both work.
More kinds
This page covers PlatformUser, Permission, and Role. The design and the rules every
kind follows live in
RFC 0001,
which also lists the kinds proposed next. The flag reference for both commands is in the
CLI reference.