Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
19b53a0
Dynamic tier: the fail-closed authorization kernel
tom-snyder Aug 22, 2026
702ad4a
Dynamic tier: four kernel fix rounds against four adversarial critics
tom-snyder Aug 22, 2026
6019da3
Dynamic tier: containment, on a host that cannot prove containment
tom-snyder Aug 23, 2026
53ea1a3
Dynamic tier: probe engine drivers, and gate 1 stops being vacuous
tom-snyder Aug 23, 2026
f02dba5
Dynamic tier: attack-surface inventory, and the kernel's first allow
tom-snyder Aug 23, 2026
0d4d470
Dynamic tier: crawl, auth, coverage, confirmation gate, record slots
tom-snyder Aug 23, 2026
e2229f0
Crawl, CRITICAL: canonicalize the path before matching it against the…
tom-snyder Aug 23, 2026
03ad406
Dynamic tier: close the crawl-and-auth and confirmation-gate review f…
tom-snyder Aug 23, 2026
b703c32
Dynamic tier: invert defence detection, and learn what "generate" act…
tom-snyder Aug 23, 2026
6e47010
Dynamic tier: close the over-broad-signature class on structure
tom-snyder Aug 23, 2026
285b2d2
Dynamic tier: ambiguity branches at the step, not at the match
tom-snyder Aug 23, 2026
ae86dc6
Dynamic tier: guards re-keyed onto their subjects
tom-snyder Aug 23, 2026
4924591
Confirmation gate: ambiguity decided from the denoted language
tom-snyder Aug 23, 2026
a5e748e
Confirmation gate: the over-broadness class is closed on the match
tom-snyder Aug 23, 2026
8a7b7f1
Confirmation gate: both directions hold
tom-snyder Aug 23, 2026
8d1f809
Confirmation gate: pin the derived floor, delete the pre-floor arithm…
tom-snyder Aug 23, 2026
03cd9cd
Confirmation gate: make the doc claims measured, and find the guard's…
tom-snyder Aug 23, 2026
30a9b28
Confirmation gate: a citation is a form, and the form works
tom-snyder Aug 23, 2026
95e70db
Confirmation gate: aptness resolved through the syntax tree
tom-snyder Aug 23, 2026
4fee89d
Confirmation gate: correct a false gate-3 doc that was true when written
tom-snyder Aug 23, 2026
81d51aa
Close the confirmation gate -- three words, and the streak ends
tom-snyder Aug 23, 2026
e06c27a
Name plan parts in words, and guard against step codes returning
tom-snyder Sep 30, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
# Without this, a Windows checkout gets CRLF working-tree files, `gofmt -l`
# reports every Go file as unformatted locally while CI (which sees LF) passes,
# and local verification stops being trustworthy. Every packet in
# plan/IMPLEMENTATION-PLAN.md owes build/test evidence, so local verification
# plan/design/first-plan.md owes build/test evidence, so local verification
# has to mean something.

* text=auto eol=lf
Expand Down
227 changes: 195 additions & 32 deletions .github/workflows/ci.yml

Large diffs are not rendered by default.

139 changes: 139 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
# golangci-lint configuration for Anvil.
#
# =============================================================================
# READ THIS BEFORE TRUSTING ANYTHING IN THIS FILE
# =============================================================================
#
# This file is NOT the authorization kernel's build-time guard. It is an
# editor- and pre-commit-speed echo of it. Two facts about this repository make
# that distinction load-bearing rather than modest:
#
# 1. .github/workflows/ci.yml does not run golangci-lint. CI runs gofmt,
# go vet, go build and `go test -race -count=1 ./...`. So on the machine
# that decides whether a change lands, the rules below are inert until a
# lint job is added -- and ci.yml is outside the build-time guard's write scope,
# so the kernel's build-time guard could not add one.
# 2. Rule `egress-choke-point` below is a DENYLIST, and a denylist loses. It
# names net, net/http, crypto/tls and the ones we thought of. It does not
# know about the networking package that appears in Go 1.28, or a
# third-party client somebody vendors next month, and its failure mode
# when it does not know is a PASS.
#
# The guards that actually hold are Go tests, they run in the CI job that
# already exists, and they are built the other way up -- they enumerate what is
# PERMITTED and refuse everything else, so a networking API nobody has heard of
# is caught by default rather than by memory:
#
# internal/dast/authz/egress_chokepoint_test.go gate 3, allowlist of inert
# imports, whole-repo scan
# internal/dast/authz/kernel_depgraph_test.go gate 2, `go list -deps`,
# resolved by import PATH
# cmd/anvil/split_test.go the two-artifact split
#
# If a rule here and a test there ever disagree, the test is right.
#
# UNVERIFIED, and recorded rather than glossed: no golangci-lint binary exists
# on the machine that wrote this file, so the rules below have never been
# executed. The YAML parses; that is the whole of what has been demonstrated
# about them. Settling condition: a lint job in ci.yml, at which point this
# comment comes out and the rules are either proven or corrected.

version: "2"

linters:
default: none
enable:
- depguard

settings:
depguard:
rules:
# -------------------------------------------------------------------
# GATE 2 -- the kernel's dependency graph must not invert.
#
# This one IS an allowlist, because depguard can express it: strict
# mode denies every import that is not listed. It mirrors
# kernelImportAllowlist in internal/dast/authz/phase0_build.go, which
# is the reviewed copy; the authoritative check is
# TestGate2KernelClosureIsStdlibPlusTheAllowlist, which walks the
# TRANSITIVE closure with `go list -deps`. depguard only sees the
# imports written in the file in front of it, so it cannot see an
# inference package reached two hops away through an innocently named
# intermediary. The test can, and has a fixture proving it.
#
# The spine's safety section: the authorization kernel is "compiled separately
# from the model runtime, with a build-time test that fails if the
# dependency graph inverts. No model ever holds a network handle."
# -------------------------------------------------------------------
kernel-dependency-graph:
list-mode: strict
files:
- "**/internal/dast/**"
allow:
- $gostd
- github.com/Susquehanna-Syntax/Anvil/internal/dast
- github.com/Susquehanna-Syntax/Anvil/internal/record
deny:
# Redundant against strict mode, and kept for the message. The spine's safety section
# names these three shapes; a contributor who trips one should be
# told which rule they tripped, not just that their import is
# unlisted.
- pkg: github.com/Susquehanna-Syntax/Anvil/internal/inference
desc: >-
the spine's safety section: the authorization kernel is compiled separately from the
model runtime. No model ever holds a network handle.
- pkg: github.com/Susquehanna-Syntax/Anvil/internal/model
desc: >-
the spine's safety section: the authorization kernel is compiled separately from the
model runtime. No model ever holds a network handle.
- pkg: github.com/Susquehanna-Syntax/Anvil/internal/llm
desc: >-
the spine's safety section: the authorization kernel is compiled separately from the
model runtime. No model ever holds a network handle.

# -------------------------------------------------------------------
# GATE 3 -- the egress choke point.
#
# EARLY WARNING ONLY. This is the denylist described at the top of the
# file, kept because a fast red squiggle in an editor stops a mistake
# earlier than a test run does, and deleted the moment it is mistaken
# for the control.
#
# The `files` list is the inverse of the two allowlists in
# internal/dast/authz/phase0_build.go: the kernel (the choke point
# itself) and the three Lane A packages that fetch advisory data and
# do not probe targets. Test files are excluded here and are NOT
# excluded by the real scanner inside internal/dast -- another reason
# this file is the echo and not the guard.
# -------------------------------------------------------------------
egress-choke-point:
list-mode: lax
files:
- "!$test"
- "!**/internal/dast/authz/**"
- "!**/internal/ingest/bootstrap/**"
- "!**/internal/ingest/poller/**"
- "!**/internal/mirror/accelerator/**"
deny:
- pkg: net
desc: >-
Every outbound connection in Anvil's dynamic tier is
constructed by internal/dast/authz, against an Authorization
that Adjudicate minted for that exact pinned address
(research/20 gate 3). A socket the kernel did not open is a
handle it did not authorize. Route it through Adjudicate and
PinnedDialAddress. If this is a non-probing fetch outside the
DAST tree, add the package to nonKernelEgressAllowlist in
internal/dast/authz/phase0_build.go with a justification.
- pkg: net/http
desc: >-
See "net" above. Construct requests through the kernel's
gated request layer, not directly.
- pkg: crypto/tls
desc: >-
See "net" above. tls.Dial is a socket like any other.
- pkg: golang.org/x/net
desc: >-
See "net" above. A networking API outside the standard library
is still a networking API; this entry exists because it is the
first thing a denylist like this one forgets.
32 changes: 16 additions & 16 deletions NOTICE
Original file line number Diff line number Diff line change
Expand Up @@ -67,12 +67,12 @@ duty is recorded now so that the packaging steps cannot ship without it.

Trivy (aquasecurity/trivy) -- Apache-2.0
Duty: Section 4(d) notice. Triggers when the container image bakes in the
Trivy binary. Status: PENDING (image build, plan step O.17).
Trivy binary. Status: PENDING (image build, the container image).
Source: research/13 row C25, verified from the LICENSE file body.

ZAP (zaproxy/zaproxy) -- Apache-2.0
Duty: Section 4(d) notice plus NOTICE-file duties. Triggers when the
anvil-dast image bakes in ZAP. Status: PENDING (plan step O.17).
anvil-dast image bakes in ZAP. Status: PENDING (the container image).
Source: research/13 row C3, LICENSE title line verified verbatim.

Syft (anchore/syft), Grype (anchore/grype), OSV-Scanner (google/osv-scanner)
Expand All @@ -82,35 +82,35 @@ duty is recorded now so that the packaging steps cannot ship without it.
honggfuzz (google/honggfuzz) -- Apache-2.0, per its COPYING file
Duty: Section 4(d) notice on redistribution. Status: PENDING.
Source: research/13 row D2. This is the permissive AFL++ substitute;
AFL++ is excluded by plan/00-SPINE.md S5 because its LICENSE is AGPL-3.0
AFL++ is excluded by the spine's exclusion list because its LICENSE is AGPL-3.0
regardless of what its README says.

Docker Engine, Docker Compose -- Apache-2.0
Duty: Section 4(d) notice IF redistributed as binaries. Anvil invokes
them; it does not currently redistribute them. Status: NOT TRIGGERED.
Source: plan/spine-b-open-licences.md A5, A6.
Source: plan/design/spine-open-licences.md, Docker Engine and Docker Compose.

Gemma 4 weights -- Apache-2.0
Duty: Section 4(d) notice if the weights are bundled or redistributed,
plus a container image LABEL. Status: PENDING and CONDITIONAL -- the
model tier itself may not exist. Plan step M0.18 decides whether Anvil
model tier itself may not exist. The gate decision decides whether Anvil
has a detection-model tier at all, and no component pick is final before
it resolves (plan/00-SPINE.md S3, S4).
it resolves (the spine's evaluate-first and component sections).
CAVEAT, and it is not optional: verify the licence of the SPECIFIC
Gemma 4 variant at selection time. Qwen is documented to split licences
by parameter count inside one family, so a sibling's licence proves
nothing about the one shipped. Source: plan/spine-b-open-licences.md A1.
nothing about the one shipped. Source: plan/design/spine-open-licences.md, Gemma 4.

Qwen/Qwen3-Coder-30B-A3B-Instruct -- Apache-2.0
Duty: Section 4(d) notice if weights are shipped, plus model-weight
METADATA. Status: PENDING. Same by-parameter-count caveat as Gemma 4.
Source: plan/spine-b-open-licences.md A7.
Source: plan/design/spine-open-licences.md, Qwen3-Coder-30B-A3B-Instruct.

go-apispec -- Apache-2.0
Duty: Section 4(d) requires the upstream NOTICE file be shipped VERBATIM,
not summarised. Its body is archived at data/LICENSES/GO-APISPEC-NOTICE
by plan step C.4. Status: PENDING.
Source: plan/spine-b-open-licences.md A2.
by the NOTICE archiving. Status: PENDING.
Source: plan/design/spine-open-licences.md, go-apispec.

================================================================================
3. ATTRIBUTION-REQUIRED DATA SOURCES (CC-BY and equivalent)
Expand All @@ -136,7 +136,7 @@ Attribution here is mandatory, not courtesy.

MITRE CVE -- CVE Terms of Use
Duty: reproduce MITRE's copyright designation. The operative text is
archived at data/LICENSES/CVE-TOU.txt by plan step C.4.
archived at data/LICENSES/CVE-TOU.txt by the NOTICE archiving.
Source: research/13 row B1.

MITRE CWE -- CWE Terms of Use
Expand Down Expand Up @@ -181,15 +181,15 @@ The Greenbone entry stands on verified evidence: Greenbone's own licence page
states that "The OPENVAS COMMUNITY FEED is a database licensed under the Open
Data Commons Open Database License version 1.0 (ODbLv1)". An earlier report
that this item was unsourced was checked and found wrong; see
plan/00-SPINE.md S8 and plan/IMPLEMENTATION-PLAN.md 2.3.
the spine's licence section and the first plan's Greenbone ruling (plan/design/first-plan.md).

Plan step C.2 owns this directory tree.
The share-alike tree owns this directory tree.

================================================================================
5. WHAT IS DELIBERATELY ABSENT
================================================================================

The following are EXCLUDED from Anvil by plan/00-SPINE.md S5, each for a
The following are EXCLUDED from Anvil by the spine's exclusion list, each for a
documented reason, and must never appear in this file or in any Anvil artifact:

CodeQL CLI terms forbid CI/CD database generation and
Expand All @@ -215,7 +215,7 @@ documented reason, and must never appear in this file or in any Anvil artifact:
Note on Gemma: Gemma 4 IS eligible and Gemma 1-3 are NOT. Gemma 4 resolves to
Apache-2.0 and is carved out of the old Gemma Terms of Use, whose remote-
restriction clause was the reason Gemma 1-3 were excluded. See
plan/00-SPINE.md S4.
the spine's component table.

================================================================================
6. PATENT POSTURE -- STATED SO IT IS NOT MISREAD
Expand All @@ -228,4 +228,4 @@ contributions and does nothing about third-party patents. US10043004B2
approximately 2035, assignee Denim Group Ltd. / Coalfire Systems) is materially
similar to Anvil's link-based correlation mechanism and remains an unresolved
risk. An outbound licence is not a freedom-to-operate instrument. See
plan/00-SPINE.md S8 and plan/40-record-and-storage.md Open Question 1.
the spine's licence section and plan/design/record-and-store.md Open Question 1.
Loading
Loading