We audit the configuration that public chains already expose, because almost nobody reads it.
One rule shapes everything here: a tool that inspects your accounts must not be able to hurt them. Every project in this org is read-only and secret-free by construction, not by convention.
| Project | What it does | State |
|---|---|---|
| Gauge | Reads a Stellar account's signers, thresholds, flags and home domain from Horizon, compares them with a policy file you declare, and exits nonzero when the two disagree. Go CLI, binary stellar-gauge. |
Unaudited, functional alpha. Documentation site live since 2026-11-02. |
| Airlock | Scans a Stellar or Soroban repository for supply-chain compromise and release-integrity gaps: what a workflow can reach, what a manifest declares, what a package's lifecycle scripts do. Python CLI, binary airlock, zero runtime dependencies. |
Unaudited, functional alpha with no Action yet. v0.0.1 is a pre-release, not a version to depend on. Documentation site live since 2026-11-02. |
| Stellar account fixtures | Nine account reads captured from Horizon, eight on mainnet and one on testnet, each beside a sidecar recording the source URL, the capture time, the ledger, the Horizon version that served it and the digest of the bytes next to it, plus the validator that recomputes every one of those claims and the capture tool that refuses a bad checksum, a wrong passphrase, a redirect and anything seed-shaped. It holds no rule, no severity and no verdict about whether an account is well configured. | Public since 2026-11-02. Nine captures, 113 tests, no tag and no documentation site. The six shape labels with no capture are printed in its README rather than papered over. |
| stellar-posture-watch | The clock rather than the gate. A composite GitHub Action that builds Gauge at a commit sha you pin, runs stellar-gauge audit against the accounts in your policy file on whatever schedule your own repository declares, and keeps one tracking issue current through the GitHub issue API. It owns no detection logic: every finding it publishes came from Gauge, and the issue body names the commit that produced it. Ten inputs, ten steps, three Python scripts on the standard library and nothing installed. |
Unaudited, functional alpha. Public since 2026-11-04 with 175 tests, no tag, and its main bound by a ruleset that was observed refusing a direct push. Its CI, roadmap gate and self-check all ran green on a GitHub runner on 2026-11-04, and it now runs itself on a schedule too: its watch.yml audits this repository's own policy file against live testnet every Tuesday and keeps one tracking issue current, so issue #6 opened on 2026-11-04 carrying one high finding from Gauge, and four runs since have agreed with it and left the body byte-identical rather than churning it. Two wording bugs of its own closed the same day, both in the pair of places that tell a reader which accounts a run read: its opening line now counts the accounts a run reached rather than the ones the policy named, and the table under the heading ## Accounts asked about keeps its Read column as the only place that says what happened to each row. Its OpenSSF Scorecard run reads 5.3 over 18 checks at its current main, and there is no documentation site. |
Gauge looks at accounts on the chain, Airlock looks at the code and CI that publish to it, the fixtures repository holds the account-shaped JSON both of them are meant to test against, and stellar-posture-watch runs Gauge on a schedule and files what it finds into one issue nobody remembered to ask about. Neither tool has been pointed at the corpus yet. The Action has written its issue: #6 in its own repository, opened by a run of its own watch workflow and left untouched by the four runs that read the same posture after it, carrying a finding that came from Gauge at a pinned commit. What stays open there is somebody else's repository doing the same. Those are the repositories' own open gates rather than finished ones. They share working conventions and no code.
The third project exists because of a method problem. A rule written against a hand-typed fixture passes until the first real account disagrees with the invention, and the fix for that is not a bigger test suite inside either tool: it is captures with provenance something can re-check, kept in a repository whose only job is keeping them honest. Two of the four above arrived on 2026-11-02, and they arrived differently. Airlock moved here after the GitHub account that published it was suspended, so its repository, its history and its documentation were recovered rather than restarted. The fixtures corpus was written here.
The fourth was written here on 2026-11-04, and it exists because a tool nobody runs is not a control. An audit that only lives in a CLI drifts from the day it is installed; the same read has to arrive on a schedule, in the repository that owns the account, with its findings somewhere a person looks.
- The roadmap is the contract. Each repository carries a
ROADMAP.md, and CI refuses to merge a change that does not update it in the same pull request. Task IDs, a changelog row and a progress table move together. - Claims are traceable or marked. Every number in a README, on the docs site and on this page is read from the code or from a named run against a named network and ledger. What has not been verified says so.
- Findings are posture, never safety. Nothing here will call an account secure. The chain shows signers, thresholds and flags; a read-only tool cannot know your intent, so it reports what it read and how far it looked.
- Failure is loud. An unreachable endpoint, an oversized or malformed response, an unknown config key, a rule that panics: all of them exit nonzero. Exit 0 means the run completed and every rule it evaluated agreed with the policy.
Each bullet carries the date its numbers were read.
- OpenSSF Scorecard for the three repositories that have a run. Gauge, read 2026-11-02: 6.8 overall.
Pinned-Dependencies,Token-Permissions,Dangerous-Workflow,Security-Policy,Dependency-Update-Tool,Vulnerabilities,Binary-Artifacts,Fuzzing,LicenseandCI-Testsscore 10;Branch-Protectionscores 4;Code-Review,Maintained,Contributors,SASTandCII-Best-Practicesscore 0. Full result. Re-read 2026-11-03 after the runs onmainata0fedb7, at9739b42, at665cb16and now atc09759d: still 6.8 over 18 checks with the same split, the result dated 10:22:36Z. Airlock has no Scorecard run yet, so no number is quoted for it. The fixtures corpus, read 2026-11-03 and re-read after itsscorecard / analysisruns onmainat3bbb0a7, atccad780, atd330ee0and now atd4fa998: still 5.2 over 18 checks, withBranch-Protectionat 4 and the same three warnings Gauge carries,mainrequiring no approvers, no codeowners review and no last-push approval.Binary-Artifacts,CI-Tests,Dangerous-Workflow,License,Security-Policy,Token-PermissionsandVulnerabilitiesscore 10;Maintained,Code-Review,Contributors,CII-Best-Practices,Dependency-Update-Tool,FuzzingandSASTscore 0, andPackagingandSigned-Releasesare -1. Three of the zeros measure what the repository is: one day old, one maintainer, no external contributor. The rest are states of the setup rather than judgements on the corpus:CII-Best-Practiceshas no badge registered,Dependency-Update-Toolhas no update bot configured,SASThas no code-analysis tool running on its commits,Fuzzinghas nothing to fuzz in a corpus of JSON it re-serialises itself, andPinned-Dependenciesis 6 because its CI installs the two dev tools by version rather than by hash. None of those measures whether a sidecar matches the bytes beside it. Full result. - Gauge has been run against testnet only, which is its own verification policy. Five recorded runs read on 2026-11-02, the latest at ledger 5517199: one WARN finding, exit 1.
- Gauge readiness, read 2026-11-03 from
mainatc09759d(the merge of PR #105 at 10:22Z): 45 of 77 tasks across Phases 0 to 6, 1 of 10 gates green. The readiness count moves with every merged task, which is why the bullet names the commit rather than just the day. v0.1.0 is Phases 0 to 4 and v0.2.0 is Phases 5 and 6. Neither tag exists yet. - Airlock readiness, read 2026-11-02: 45 of 95 tasks across Phases 0 to 8, 0 of 10 gates green. 709 tests pass with one skipped, and
mainscans its own repository with no HIGH and no WARN findings. Twenty-two rules in four rule files; the composite Action, the Stellar-side detectors and SARIF output are not built, which is what its pre-release notes say. - Fixtures corpus readiness, read 2026-11-03 from its
mainatd4fa998(the merge of its PR #26 at 10:02Z): 17 of 24 tasks across Phases 0 to 3, its own Phase 1 complete at 9 of 9 and its Phase 2 coverage at 3 of 6, 2 of 5 gates green. 113 cases pass, 95 of the corpus and its capture path plus 18 that run the two roadmap gate scripts as subprocesses over a temporary repository, andvalidate.pyexits 0 on Python 3.11.16 and 3.12.3 locally and on a 3.11.17 runner in its own CI, most recently onmainatd4fa998with the sidecar, tests, lint, format and types steps each reporting success. Itsci.ymlnames both suites rather than trusting discovery, because default discovery starts at the repository root and skips a directory that is not a package.mainrefuses a direct push and both of its checks are required status checks. Nine accounts, eight mainnet and one testnet, every sidecar recording the same Horizon build 30.0.0, the mainnet reads carryinglast_modified_ledgerfrom 65110665 to 65282653 and the testnet one 5535101. Eighteen of its 24 declared shape labels have a capture; the six that do not are listed rather than worked around with invented data, and no consumer reads it yet. Its open backlog is 10 issues: three of its gates, the three remaining Phase 2 searches, the three Phase 3 consumer tasks and one Phase 0 decision about the scenario numbers a sidecar carries. - Posture-watch readiness, read 2026-11-04 from its
mainat7ef214e(the merge of its PR #9 at 10:12:13Z): 32 of 38 tasks across Phases 0 to 3, its own Phase 0 complete at 7 of 7, its Phase 1 at 10 of 11, its Phase 2 at 11 of 13 and its Phase 3 at 4 of 7, with a four-task backlog that the total does not count, 4 of 5 gates green. The reading earlier the same day, ate502392, was 30 of 37 with its Phase 3 at 2 of 6, and what moved between them are the two wording tasks it found while closing its G3 and G4 gates, both now closed. 3.6 went in with PR #8 (4fad8b0): the opening line counted the accounts the policy named and called them read, so a run that asked about three and reached two said "across 3 accounts read". 3.7 went in with PR #9 one section lower, the same claim in a different place: the heading## Accounts readsat above a table whose own last column prints(not read), so the title contradicted the row directly underneath it, and the heading is now## Accounts asked aboutwith theReadcolumn left as the only place that says what happened to a row. A section name is a body-contract string in this repository, keyed on by five test split sites, so the rename also movedREADME.md's schematic and the paragraph under it, the display-only row ofARCHITECTURE.mdsection 2's field table and the fourth constraint inCONTRIBUTING.md, and it came with a test that refuses any account heading claiming read while a row beneath it disagrees. The other new test is the one that justifies the size of the change: it re-renders a body whose heading has been rewritten back to the old title and the run reports no change, because the state marker is keyed on the finding fingerprints and not on the wording around them. Two tests, two pull requests, one for each of 3.6 and 3.7, rather than one commit that moves the sentence and the heading together. The hosted renders moved by exactly the seven characters of the heading.self-check / actionjob 126494824709 on the 3.6 branch at60b842ewrote 2308 characters, and jobs 126503764248 and 126514135115 on the two 3.7 commits each wrote 2315, and all three reportno change since the previous run (1 open, 1 high, 0 warn, 0 accepted)and end withsync_issue: issue #6 needs no change, left alone. That is the no-change path read on a runner rather than asserted in a test, and read now across a sentence change, a heading change and a docs-only commit, which is the D-07 marker claim doing work instead of being described. 175 cases pass on Python 3.11.16 and 3.12.3 locally (8.995s and 10.600s, both read today), and on its own CI, which asks for3.11and takes whatever patch the image carries: most recently onmainat7ef214e,ci / pythonjob 126515324984 resolving CPython 3.11.18, the patch having moved from the 3.11.17 this page named that same morning, withcheck_action: action.yml holds its rules,Ran 175 tests in 6.880s,All checks passed!,10 files already formattedandSuccess: no issues found in 10 source files.mainrefuses a direct push and both of its checks are required status checks under ruleset 26515104, which is the corpus's ruleset copied rather than re-argued. Its OpenSSF Scorecard runs at7ef214eare a pair rather than a number, and the pair is the part worth reading:scorecard / analysisjob 126515324448, result dated 10:12:26Z, returned 5.2 withCI-Testsat -1 and a reason naming a 502 from the commits-and-statuses API, and the same workflow re-run on the same commit as job 126522775661 at 10:32:16Z returned 5.3 withCI-Testsat 10 on 8 of 8 merged pull requests checked. Nothing in the repository moved between the two readings, so a scorecard result measures an API call as well as a tree;https://api.scorecard.dev/projects/github.com/posture-sec/stellar-posture-watchanswers the 10:32:16Z reading, which is the one the badge renders, and that is the 5.3 quoted here. Against the 5.3 this page quoted at4fad8b0(job 126497337181, 09:24:57Z) the only movement is in the two checks that count merged pull requests:CI-Testsfrom 7 of 7 to 8 of 8 andCode-Reviewfrom 0 of 8 to 0 of 9 approved changesets, each at the same score either way.Binary-Artifacts,CI-Tests,Dangerous-Workflow,License,Security-Policy,Token-PermissionsandVulnerabilitiesscore 10;Branch-Protectionis 4 for the same three warnings the other two repositories carry, no approvers required, no codeowners review and no last-push approval, which is what a single maintainer looks like to that check;Maintainedis 0 because the repository was created this month;Pinned-Dependenciesis 7, with 8 of 8 GitHub-owned and 1 of 1 third-party action dependencies pinned against the onepipCommandatci.yml:45that is not hash-pinned;Contributors,CII-Best-Practices,Dependency-Update-ToolandSASTare 0, which is one maintainer, no registered badge, no update bot and 0 of 21 commits checked by a code-analysis tool rather than four findings;Fuzzingis 0 for a reason that does measure something here, because nothing hammersrender_report.pyeven though it parses a report whose contents came off the chain; andPackagingandSigned-Releasesare -1 because nothing is published from the repository at all, which is also what its untagged state means. Worth naming what those scores did not do. The repository now carries a workflow whose token can write issues, andDangerous-WorkflowandToken-Permissionsboth still read 10 on the commit that added it, because that check's sensitive-permission warnings namecontents,actionsandstatusesand notissues, so a 10 there is not evidence about the write. What none of the eighteen measures is the gate still open, G5: nobody other than the maintainer has run it. Its own remaining rows are 1.9, which is an observed exit-3 path on a runner rather than a fixture, the documentation site (2.9), the first tag (2.10, whose blocker cleared on 2026-11-04), one mainnet run by the owner (3.3), a second consumer (3.4), the wave backlog (3.5, unpublished until an application is approved) and the four deferred backlog tasks. Full result. - One maintainer and no external contributions yet, which is why
ContributorsandCode-Reviewscore 0 for Gauge, for the fixtures corpus and for posture-watch, and why none of the threemainbranches requires an approving review. Those are honest numbers, not good ones.
Each repository states its own scope and disclosure path: Gauge's SECURITY.md, Airlock's SECURITY.md and the corpus's SECURITY.md and the Action's SECURITY.md. In the corpus the reportable thing is a false claim about provenance rather than an account: bytes that do not match their sidecar, seed-shaped material anywhere in the tree, or a validator that exits 0 on a corpus it should refuse. Read the status section of whichever repository you are reporting against first: it tells you whether what you found is a bug or a limitation that is already written down.