Working name Gauge, binary stellar-gauge. It reads the public configuration of your Stellar accounts and compares it with what you said you expect. Signers, thresholds, flags, home domain. It reports the difference and exits nonzero when the difference matters, so a scheduled run in CI catches a change to a treasury or issuer account the day it happens instead of the day it costs something.
The problem it addresses: an account's control settings live on-chain, are public, and almost nobody reviews them. A signer added during an incident stays forever. A threshold that no longer matches the key layout makes a "2-of-3" account 1-of-1. A clawback flag appears without anyone declaring it. These are configuration facts, so they are checkable without touching a private key.
Gauge is read-only and secret-free. It never accepts, stores or echoes a secret seed, never builds or submits a transaction, and treats every value it fetches as hostile input.
Functional alpha (ROADMAP Phase 2, tasks 2.1 to 2.10), plus the account and flag rules from Phase 3 (tasks 3.2 and 3.3). The policy loader, Horizon client, report renderers, orchestrator, rules and CLI are implemented and tested:
go run ./cmd/stellar-gauge version
go run ./cmd/stellar-gauge check --network testnet --endpoint https://horizon-testnet.stellar.org G...A run reads the account, prints what the chain shows and exits 0, 1, 2 or 3. A missing account or an endpoint on the wrong network is a HIGH finding and exits 1 by default, as does any run that could not complete. Declared flag drift and an undeclared clawback are warnings and exit 1 under --fail-on warn. The remaining twelve rules and role-based severities arrive later in Phase 3. There is no release to install.
Sixteen rules in v0.1, each with a stable kebab-case ID. Four are implemented today: acct-missing, acct-network-mismatch, flag-drift and flag-undeclared-clawback. Severity depends on the account's declared role (issuer and treasury are held to a tighter bar than merchant and distribution) once role profiles land in task 3.4; until then the four rules use their baseline severity.
| Family | Rules |
|---|---|
| Account | acct-missing, acct-network-mismatch |
| Signers | sig-no-signing-weight, sig-threshold-unreachable, sig-threshold-order, sig-single-key-high, sig-hashx, sig-unexpected-signer, sig-missing-expected, sig-weight-drift |
| Thresholds and flags | thr-drift, flag-drift, flag-undeclared-clawback |
| Identity | id-no-home-domain, id-toml-unreachable, id-toml-account-unlisted |
Exit codes: 0 pass, 1 findings at or above --fail-on, 2 configuration or usage error, 3 source or internal error. Anything unreachable, oversized, malformed or ambiguous fails closed. It never exits 0 on a check it could not complete.
- Key custody. On-chain data cannot show who holds a key, how it is stored, or whether it leaked.
- Whether an account is a good account. Gauge reports posture against your declared expectations. It never says an account is secure or safe, and "no findings" is not a clean bill of health.
- Transactions, balances, offers, claimable balances, liquidity pools and Soroban contract accounts.
- Live streaming or alerting. It is a state comparison you schedule, not a daemon.
- The expectations themselves. If an attacker can edit both
gauge.tomland the workflow that runs Gauge, the check can be made to pass. That residual risk is documented inARCHITECTURE_ESSENTIALS.mdsection 10 and is a process problem, not a code problem.
stellar-gauge check --network testnet --endpoint https://horizon-testnet.stellar.org G... G...
stellar-gauge audit --config gauge.toml --format json
stellar-gauge versioncheck takes accounts on the command line and needs an explicit network and one endpoint. audit reads accounts, roles and expectations from the policy file, whose endpoints entry supplies the endpoint. Both take --format text|json and --fail-on high|warn; markdown output is not implemented yet.
gauge.example.toml shows the policy shape: one [[account]] block per audited account with its role and expected signers, thresholds and flags, plus [[accept]] entries for risks you have decided to live with. An omitted expectation is not a zero or a false; it means "not declared", and drift rules only fire where you declared something.
policy loader + Source + sep1 -> snapshots -> rules -> accept filter -> report -> exit code
| Package | Responsibility |
|---|---|
internal/model |
Shared types: snapshots, findings, reports, severities |
internal/strkey |
Address classification and secret-seed rejection |
internal/threshold |
Authorization math: reachability and minimum signers |
internal/sanitize |
Strips control, bidi and zero-width characters from chain data |
internal/policy |
Strict gauge.toml decode, roles, accepted risks |
internal/source |
Horizon client behind a Source interface |
internal/sep1 |
Guarded stellar.toml fetch for identity rules |
internal/rules |
Pure rule functions, no I/O |
internal/report |
Text and JSON renderers, exit-code mapping |
internal/run |
Orchestration |
Imports point one way: model <- strkey, threshold, sanitize <- policy, source, sep1 <- rules <- report <- run <- cmd. Dependencies: Go standard library plus one TOML parser. No framework, no plugin system, no rule language.
PRD.md: scope, users, requirementsARCHITECTURE.md: design, data models, decision records, red-team reviewARCHITECTURE_ESSENTIALS.md: the hard rules an agent must not breakROADMAP.md: every remaining task, progress and changelogproject_structure.md: where files go and who may change themAGENTS.md: working agreement for coding agents
| Maintainer | GitHub | Contact |
|---|---|---|
| Ademola | @bolabeatz | GitHub Discussions |
Read CONTRIBUTING.md. Every pull request updates ROADMAP.md in the same change, and every rule ships with a positive and a negative fixture.
Apache-2.0. See LICENSE. This code is unaudited; see SECURITY.md.