Skip to content

Operator guide

How to build, configure and start the Bronzeward proof of concept, and where to go next. The walkthrough then takes one existing cluster through adoption, publication, approval, a safe apply and drift handling.

Bronzeward is a proof of concept. Its releases are pre-releases (v0.N.0-alpha.M), never a production version, and none is published until the licence is decided (ginsys/bronzeward#16). The supported scope is design §18.2, and the acceptance plan defines what must be checked.

The guide has three parts:

The profile

The PoC supports one deployment profile (design §7.7):

  • PostgreSQL as the database (evidence on 17.11), one server;
  • OpenBao KV v2 and Transit as the secret and artifact-encryption provider (evidence on 2.6.1), one node, one unseal share;
  • Talos clusters reached directly through each node's Talos API (design baseline v1.13.6).

No other database, provider or topology is supported. Replication, failover, auto-unseal and several Bronzeward instances behind one address are unmeasured. You run, secure and back up PostgreSQL, OpenBao and the store holding Bronzeward's provider tokens yourself (design §7.7, "Operator responsibilities").

Two ways to run it

  • The disposable fixture (fixtures/): a two-node Talos cluster, PostgreSQL, OpenBao, an OIDC issuer with synthetic humans and three server instances, all in Docker on one Linux host. Every secret is synthetic. This is the environment the walkthrough is written for, and the one a reviewer should use.
  • Your own deployment: the same binary and configuration file against your own services. Each walkthrough step ends with a note on what differs.

Build

Prerequisites: Go at the version mise.toml pins, and mise (or run the go build lines below yourself). The fixture needs more; see its prerequisites.

mise trust            # once per fresh checkout: mise refuses an untrusted mise.toml
mise run build        # bin/bronzeward and bin/oidc-fixture
# without mise, the same two builds:
go build -o bin/bronzeward ./cmd/bronzeward
go build -o bin/oidc-fixture ./fixtures/oidc

bin/bronzeward is the server and its operator commands: serve, migrate, token and orphans. Each takes -config <file>.

Configure

Start from examples/bronzeward.yaml, whose comments name each setting and its contract. The DSN carries a password, so make your copy readable only by the user that runs Bronzeward before filling it in:

install -m 600 examples/bronzeward.yaml bronzeward.yaml

The blocks:

Block What you set
listen The address to serve on. Plain HTTP: terminate TLS in front of it.
database.dsn Your PostgreSQL DSN, with credentials.
auth.oidc Your identity provider's issuer, the audience, the groups claim, the longest token lifetime accepted (default 15 minutes).
auth.roles Which groups grant viewer, author, publisher, approver and recovery-admin.
auth.deniedSubjects Subjects refused from configuration, which survives a database restore; list a subject here before revoking it: a human as {iss: ..., sub: ...}, a service identity as {identity: idn_..., name: ...}, both in one entry (persistence API §10.4).
execution maxTransportDeadline (required), and the defaults a plan binds when its request leaves a value out.
provider OpenBao's address, the four Transit key names and one token file per identity. Without it, ingestion and publication answer 503.
ingestion Required with provider: this instance's name and the claim and job timers.

OpenBao needs KV v2 at secret/, Transit, the four Transit keys, and one token per identity under its own policy. The fixture's policies are the reference: fixtures/openbao/policies, described in OpenBao keys and policies. Each token file is mode 0600 and named by an absolute path.

Each cluster's Talos client configuration is a provider secret you write, never a Bronzeward request (persistence API §3.3):

bao kv put -mount=secret access/talos/<cluster id> talosconfig=@talosconfig

<cluster id> is the cl_... identifier Bronzeward returns when the cluster is recorded (walkthrough, S1).

Migrate, then serve

bin/bronzeward migrate -config bronzeward.yaml
bin/bronzeward serve -config bronzeward.yaml  # runs until stopped

From another shell:

curl -i http://127.0.0.1:8080/livez           # 204 once serving

migrate is the only way the schema changes. Run it with every server stopped. It applies the binary's migrations, records the installation epoch on first use, and prints the schema is at version N, exactly this binary's migrations. serve refuses a schema that is not exactly its own.

serve -recovery-start is only for starting after a database or provider restore. It is covered with recovery, not here.

Identities and roles

Humans authenticate with a JWT from your identity provider, sent as Authorization: Bearer <token> on every request. Their roles come from the groups claim through auth.roles. approver and recovery-admin are held only by humans.

Automation uses a token Bronzeward issues. The token tool talks to the database directly, so run it on the server host:

bin/bronzeward token issue -config bronzeward.yaml -name ci -roles author,publisher \
  -responsible <human subject> -operator <your subject>
bin/bronzeward token list -config bronzeward.yaml
bin/bronzeward token rotate -config bronzeward.yaml -identity <idn_...> -operator <your subject>
bin/bronzeward token revoke -config bronzeward.yaml -identity <idn_...> -operator <your subject>

issue and rotate print the token once, on stdout. Roles are viewer, author and publisher. A token lives 30 days unless -expires says otherwise, at most 2160h.

Start the fixture

fixtures/bin/up            # about two minutes once images are cached
fixtures/bin/bw migrate    # before any instance starts: serve refuses an unmigrated database
fixtures/bin/inject start A
fixtures/bin/inject start B
fixtures/bin/inject start C
fixtures/bin/seed          # the automation identity; its token goes to fixtures/.state/automation-token
curl -i http://127.0.0.1:28080/livez

What up makes, from fixtures/versions.env:

Service Host address
Instance A (the one the walkthrough calls) http://127.0.0.1:28080
Instance B http://127.0.0.1:28081
Instance C (the interruption build) http://127.0.0.1:28082
PostgreSQL 127.0.0.1:25432
OpenBao http://127.0.0.1:28200
Talos control plane, worker 10.55.0.2, 10.55.0.3 (on the Docker network)

The OIDC issuer is not published. Tokens are minted offline from its key with the image's oidc binary, as the walkthrough shows. Its synthetic humans are h-author, h-publisher, h-approver, h-recovery and h-viewer, each holding the role it names, and h-all, holding author, publisher and approver. A minted token lives five minutes.

fixtures/bin/down removes everything, including fixtures/.state/. Copy out what you want to keep first.

Next

  • The walkthrough: adopt the fixture's cluster, publish, approve, apply and handle drift.
  • Recovery: stop a server, settle an interrupted operation, restore the database or OpenBao.
  • Limits: what the PoC covers, what it does not, and its known gaps.
  • Troubleshooting: read a refusal and a machine's timeline.