Policy
Declare what should be true.
A policy is one TOML file. It names the network, one endpoint and the accounts you audit, then says which values you expect. Gauge compares the file with the chain and reports the difference. Nothing in the file is applied to the chain.
A worked file
This file is valid against the v0.1 loader. The account is the public SEP-23 test vector, not a real treasury.
# The only version this release accepts. version = 1 # Required. mainnet, testnet or a full network passphrase. network = "testnet" # Exactly one endpoint is read at v0.1. endpoints = ["https://horizon-testnet.stellar.org"] [[account]] # Canonical G address. M and C addresses are rejected. id = "GA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVSGZ" name = "treasury" role = "treasury" expected_signers = [ { key = "GA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVSGZ", weight = 1 }, ] expected_thresholds = { low = 1, medium = 2, high = 2 } expected_flags = { auth_required = false, auth_revocable = false, auth_immutable = false, clawback = false } home_domain = "example.com" expect_locked = false # Only the owner adds accept entries, and always with a reason. # [[accept]] # rule = "flag-drift" # account = "GA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVSGZ" # reason = "Observed change is scheduled for the November rotation." # expires = "2027-04-27"
The file is read from --config for audit. The check command takes the same facts as flags and has no policy file.
Top-level keys
| Key | Required | Accepted values |
|---|---|---|
| version | Yes | The integer 1. Any other value is rejected. |
| network | Yes | mainnet, testnet or a full passphrase such as Test SDF Network ; September 2015. |
| endpoints | Yes | An HTTPS URL with a host, no userinfo, query or fragment. More than one entry is rejected because comparison is not implemented. |
| account | 1+ | One or more [[account]] tables. The same G address may not appear twice. |
| accept | No | Zero or more [[accept]] tables, each covering one rule for one account. |
Account keys
| Key | Type | Behavior |
|---|---|---|
| id | G address | Required. The canonical StrKey account ID. Multiplexed M and contract C addresses are refused with a specific message. |
| name | String | Optional operator label. Gauge does not use it in any comparison. |
| role | String | issuer, treasury, distribution, merchant or other. Defaults to other. Role severity overrides are not applied yet. |
| expected_signers | Array | Objects with a key and an integer weight from 0 to 255. Keys may be G, T, X or P strkeys. A duplicate key is rejected. |
| expected_thresholds | Table | Any of low, medium and high, each an integer from 0 to 255. Omitted tiers are not compared. |
| expected_flags | Table | Any of auth_required, auth_revocable, auth_immutable and clawback, each a boolean. Omitted flags are not compared. |
| home_domain | String | A bare host name. URLs, IP addresses, a trailing dot and characters such as /, @, : and ? are rejected. Declaring it is what lets id-no-home-domain fire when the chain shows none. |
| expect_locked | Boolean | Declares that a zero-weight, nonsigning issuer is intended. Parsed today; no live rule reads it yet. |
Most expectations do not run yet. Seven rules are live: they read the network, the account's existence, its flags and its published stellar.toml. A declared home_domain is compared today, by id-no-home-domain, and an account that publishes one is fetched and checked. expected_signers, expected_thresholds and expect_locked are validated and stored, but no shipped rule compares them. Declaring them now prepares the file for later tasks without changing today's result.
Omitted is not false
The loader keeps every expectation as a pointer, so it can tell "not declared" apart from a declared zero, empty or false value. That distinction decides whether a rule runs at all.
| In the file | Read as | Effect on the live flag rules |
|---|---|---|
| (no key) | Not declared | No comparison. flag-undeclared-clawback still fires if clawback is enabled, because the omission is the point. |
| false | Declared false | The chain value is compared. A chain value of true becomes flag-drift and suppresses the undeclared warning. |
| true | Declared true | The chain value is compared. A chain value of false becomes flag-drift. |
| 0 | Declared zero | A real threshold or weight, not an omission. It is compared as stored. |
An empty expected_signers = [] declares that the account should have no signers. It is not the same as leaving the key out, which declares nothing.
Accepting a known risk
An acceptance marks one finding as known without hiding it. The report keeps the finding, labels it accepted, and leaves it out of the exit-code decision.
| Field | Rule |
|---|---|
| rule | A concrete rule ID. Wildcards, a leading or trailing hyphen and the three structural rules below are refused. |
| account | The G address the finding belongs to. The match is rule plus account, so one entry covers one finding type for one account. |
| reason | Required and non-empty. It is stored in the policy, not printed in the report footer. |
| expires | Optional YYYY-MM-DD. The date is inclusive and evaluated in UTC, so an entry expires at the start of the following day. |
Three rules can never be accepted, because accepting them would silence a structural failure rather than a judgement call:
The owner adds acceptances, not the operator running the check. An acceptance is a dated statement that a disagreement is understood. If the reason cannot be written down, the finding should be fixed instead.
The loader is strict
A policy that cannot be parsed exactly is rejected before any network call, and the run exits 2. The rules are deliberate:
- At most 1 MiB is read. A larger file is rejected, not truncated.
- Unknown keys are errors. A typo cannot silently drop an expectation.
- A duplicate account ID or duplicate rule-and-account acceptance is an error.
- Every token in the raw file is scanned for a valid
Sseed, including comments and keys the schema does not define. A match is rejected and the seed is never printed. - Errors name a field path such as
account[2].expected_thresholds.high, never a value from the file. - Endpoints are checked with the same rule the HTTP client uses, so a file cannot name an endpoint the client would refuse.
A secret seed in a policy file is a key-handling incident. Remove it, rotate the key and check the file's history. The message says to rotate for that reason.
Endpoint rules
One endpoint per run. HTTPS is required, with plain HTTP allowed only for localhost and loopback addresses, which exist for local tests. The URL may not carry credentials, a query string or a fragment. Redirects are not followed, so a URL that answers with a redirect is a source failure, not a silent hop to another host.
See Mechanics for the network check and the request limits, or Guides for a runnable policy and CI wiring.
The boundary
A policy says what you expected. The report says what the chain shows. Neither is a guarantee that the account cannot be compromised. Gauge documentation
The policy file is an input to a comparison. It is not enforced on-chain, it cannot prevent a signing operation, and it has no authority if someone else controls the keys or the CI job that runs it.