feat: Go port of SEC-AF — installing this repo now gives you the Go node - #130
Merged
Conversation
The shared substrate every ported package builds on: afx (map<->struct binding, the _unwrap/_as_dict envelope helpers, exclude_none), pyfmt (Python round/str/repr/json.dumps parity), appx (the Harness/AI/Note/Call agent seam + recording test fake), harnessx (generic structured-harness runner resolving committed pydantic schema fixtures by Go type name), aix (the .ai(schema=) path with the Python SDK's strictify, salvage and parse-retry semantics), byte-verbatim embedded prompts with a drift test against the Python tree, config (env parity incl. precedence quirks), schemas (every pydantic model with default-seeding UnmarshalJSON, pydantic-isoformat timestamps, uuid4 constructors) and scoring. Fixtures and goldens are generated from the live Python models by the committed scripts under go/scripts and pinned by parity tests. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
recon mappers (+ deterministic _parsers port and repo metrics), the scan->enrich hunter pipeline for all 12 strategies, fingerprint+AI dedup and chain correlation, the prove sub-agents (tracer, sanitization, exploit, verdict via .ai(), assembler, chain builder, dast, cross-service, dep reachability, sandbox), remediation, recon-context pruning with the language/framework hint tables, the AI gates with transient-error retry, the static compliance mapping with the hybrid AI fallback and gap report, the SARIF/JSON/Markdown/compliance-report emitters, diff analysis, monitoring baselines and policy evaluation. Prompt builders are golden-tested byte-for-byte against the Python implementations; output artifacts are golden-tested against fixtures generated by the committed gen_golden scripts. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
internal/phases carries recon/hunt/prove/remediation_phase — the reasoners the audit drives through tracked app.Call invocations with the exact Python targets, kwargs, semaphore bounds and notes, so the control-plane DAG is identical to the Python node's. internal/orch ports AuditOrchestrator: checkpoints, budget/cost bookkeeping, the phase harness proxy, reachability assessment, drop tracking and generate_output (severity floors, exploitability scores, hybrid compliance, SARIF/report generation), including the documented Python bugs reproduced bug-for-bug (compliance-report arity, inert enable_dast, dast-verifier arity). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
internal/reasoners registers the audit reasoner plus the 33 router reasoners under their exact Python names, tags and signature-derived input schemas (captured from a live Python node and embedded as the parity fixture). internal/node ports app.py: agent construction from the environment (AGENTFIELD_URL precedence, callback URL, harness + AI config), the audit handler with its four phase calls, checkpointing and error mapping, and the repo resolver (clone/pull semantics). cmd/sec-af is the entry point, default node id sec-af on :8013. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…sec-af The root manifest gains superseded_by pointing at go/, whose manifest declares the same name and node id, so a git install lands the Go node and replaces an existing Python install in place (same id, same triggers, secrets kept); a local-path install remains the documented Python escape hatch. Adds the multi-stage Go Dockerfile (checksum- verified AForge fetch + opencode, non-root user), the model-aware entrypoint, a compose add-on that joins the Python stack under the sec-af-go id, Makefile, go/README, a root-README section and a Go CI workflow (build/vet/test/gofmt + docker build). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Complete 1:1 port of SEC-AF from Python to Go on the AgentField Go SDK, following the porting pattern established by pr-af (Agent-Field/pr-af#53, #54, #64) and SWE-AF (Agent-Field/SWE-AF#94). The Python implementation is byte-untouched — every diff lives under
go/, plusdocker-compose.go.yml, one root-README section, the root-manifest redirect, and a new Go CI workflow.Like pr-af's end state, the Go node takes the product's name: the root manifest declares
superseded_by: …//go, soaf install https://github.com/Agent-Field/sec-afinstalls the Go package assec-af(default port 8013) and replaces an existing Python install in place — same node id, same triggers, node-scoped secrets kept. Installing a cloned checkout as a local path remains the documented Python escape hatch.docker-compose.go.ymlis an add-on that runs the Go node assec-af-gobeside the Python stack during a changeover.Parity contract honored
auditplus the 33 router reasoners tagged["security","audit","red-team"]— each publishing the same input schema the Python SDK derives from the function signature (captured from a live Python node into a committed fixture; a parity test reads them back through/discover).app.call(f"{NODE_ID}.x", …), Go does a trackedagent.Callwith identical targets and kwargs —audit → recon/hunt/prove/remediation_phase → mappers / hunters+dedup / verifiers / remediation, same gather shapes and semaphore bounds, so the control plane draws the identical workflow DAG.model_dump()key sets and defaults reproduced (default-seedingUnmarshalJSON,exclude_noneat the same call sites, pydantic-isoformat timestamps, uuid4 id constructors).go/scripts/).TypeError, the inertenable_dastparameter, the dast-verifier arity mismatch,_repo_metrics' skip-dir behavior, the hunter TypeError-cascade's effective signatures, the split 400/500 error mapping (only the checkpointValueErroris inside Python'stry), and both_default_strategiesvariants..ai()parity: the structured-output path reproduces the Python SDK's tolerant parsing (direct parse → greedy{.*}salvage → up to 2 request retries →Could not parse structured response: …) — found by live testing when a model wrapped its JSON in a markdown fence.Verification
go build ./… && go vet ./… && go test ./… && go test -race ./…green;gofmt -lempty; ~700 Go test functions, all derived from the Python tests/behavior (goldens + fixtures generated from the live Python models by committed scripts). Also run on the CI-pinned go1.21 toolchain.opencodeharness; the sameaudit(depthstandard) triggered through the async execute API on a seeded vulnerable fixture repo..ai()gates): edge multisets identical; only theremediation_phase→run_remediationfan-out moves, becauserun_verdict_agentis a live LLM call whose verdicts decide remediation — the same nondeterminism the Python node has.run_architecture_mapperexecuted on the Go node with the realopencodeCLI andopenrouter/moonshotai/kimi-k2.5, returning a schema-validArchitectureMapof the fixture repo.docker build -f go/Dockerfile .from the repo root; aforge fetch checksum-verified); image smoke-tested (non-root user, entrypoint generatesopencode.jsonfromHARNESS_MODEL).git diff origin/main -- src tests pyproject.toml Dockerfile docker-compose.ymlis empty.Known accepted differences (documented in code)
/healthbody is{"status":"ok"}vs Python's{"status":"healthy",…}— every consumer only checks the status code.AGENT_CALLBACK_URLunset: Go lets the SDK derivelocalhost:<actual port>instead of reproducing Python's fallback that advertises a port the node may not listen on.test_scoring.py) contradict the currentscoring.py; the port pins the code's interpreter-verified behavior and flags the tests.🤖 Generated with Claude Code