Skip to content
@posture-sec

Posture Security

Read-only posture audits for public chains. A tool that inspects your accounts must not be able to hurt them.

posture-sec

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.

Projects

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.

How the work runs

  • 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.

What has been observed

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, License and CI-Tests score 10; Branch-Protection scores 4; Code-Review, Maintained, Contributors, SAST and CII-Best-Practices score 0. Full result. Re-read 2026-11-03 after the runs on main at a0fedb7, at 9739b42, at 665cb16 and now at c09759d: 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 its scorecard / analysis runs on main at 3bbb0a7, at ccad780, at d330ee0 and now at d4fa998: still 5.2 over 18 checks, with Branch-Protection at 4 and the same three warnings Gauge carries, main requiring no approvers, no codeowners review and no last-push approval. Binary-Artifacts, CI-Tests, Dangerous-Workflow, License, Security-Policy, Token-Permissions and Vulnerabilities score 10; Maintained, Code-Review, Contributors, CII-Best-Practices, Dependency-Update-Tool, Fuzzing and SAST score 0, and Packaging and Signed-Releases are -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-Practices has no badge registered, Dependency-Update-Tool has no update bot configured, SAST has no code-analysis tool running on its commits, Fuzzing has nothing to fuzz in a corpus of JSON it re-serialises itself, and Pinned-Dependencies is 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 main at c09759d (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 main scans 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 main at d4fa998 (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, and validate.py exits 0 on Python 3.11.16 and 3.12.3 locally and on a 3.11.17 runner in its own CI, most recently on main at d4fa998 with the sidecar, tests, lint, format and types steps each reporting success. Its ci.yml names both suites rather than trusting discovery, because default discovery starts at the repository root and skips a directory that is not a package. main refuses 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 carrying last_modified_ledger from 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 main at 7ef214e (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, at e502392, 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 read sat 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 about with the Read column 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 moved README.md's schematic and the paragraph under it, the display-only row of ARCHITECTURE.md section 2's field table and the fourth constraint in CONTRIBUTING.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 / action job 126494824709 on the 3.6 branch at 60b842e wrote 2308 characters, and jobs 126503764248 and 126514135115 on the two 3.7 commits each wrote 2315, and all three report no change since the previous run (1 open, 1 high, 0 warn, 0 accepted) and end with sync_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 for 3.11 and takes whatever patch the image carries: most recently on main at 7ef214e, ci / python job 126515324984 resolving CPython 3.11.18, the patch having moved from the 3.11.17 this page named that same morning, with check_action: action.yml holds its rules, Ran 175 tests in 6.880s, All checks passed!, 10 files already formatted and Success: no issues found in 10 source files. main refuses 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 at 7ef214e are a pair rather than a number, and the pair is the part worth reading: scorecard / analysis job 126515324448, result dated 10:12:26Z, returned 5.2 with CI-Tests at -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 with CI-Tests at 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-watch answers 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 at 4fad8b0 (job 126497337181, 09:24:57Z) the only movement is in the two checks that count merged pull requests: CI-Tests from 7 of 7 to 8 of 8 and Code-Review from 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-Permissions and Vulnerabilities score 10; Branch-Protection is 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; Maintained is 0 because the repository was created this month; Pinned-Dependencies is 7, with 8 of 8 GitHub-owned and 1 of 1 third-party action dependencies pinned against the one pipCommand at ci.yml:45 that is not hash-pinned; Contributors, CII-Best-Practices, Dependency-Update-Tool and SAST are 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; Fuzzing is 0 for a reason that does measure something here, because nothing hammers render_report.py even though it parses a report whose contents came off the chain; and Packaging and Signed-Releases are -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, and Dangerous-Workflow and Token-Permissions both still read 10 on the commit that added it, because that check's sensitive-permission warnings name contents, actions and statuses and not issues, 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 Contributors and Code-Review score 0 for Gauge, for the fixtures corpus and for posture-watch, and why none of the three main branches requires an approving review. Those are honest numbers, not good ones.

Reporting a problem

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.

Popular repositories Loading

  1. guage guage Public

    Read-only posture audits for Stellar accounts. A Go CLI that compares signers, thresholds, flags and home domain with a declared policy and exits nonzero when they disagree. Never holds a key, neve…

    Go 1

  2. .github .github Public

    Organisation profile and default community health files for posture-sec.

  3. airlock airlock Public

    Command-line scanner for supply-chain compromise and release-integrity gaps in Stellar and Soroban repositories. Reads tracked files and never executes them. No runtime dependencies.

    Python

  4. stellar-account-fixtures stellar-account-fixtures Public

    Public Stellar account reads captured from Horizon, each with the provenance its sidecar has to keep: the fixture corpus Gauge and Airlock test against.

    Python

  5. stellar-posture-watch stellar-posture-watch Public

    Composite GitHub Action: build stellar-gauge at a pinned sha, audit Stellar accounts on a schedule, and keep one tracking issue current. Read-only, dry-run by default.

    Python

Repositories

Showing 5 of 5 repositories

People

This organization has no public members. You must be a member to see who’s a part of this organization.

Top languages

Loading…

Most used topics

Loading…