Ferrum GitForgeOps
Your gateway, as code.
A repository template and CLI that turns a GitHub repository into the control plane for your Ferrum Edge gateways. Resources live as YAML, pull requests get a validated plan and policy review against the live gateway, merges apply per environment after approval, and scheduled checks report API drift after approval or through a separate monitor environment. Everything runs on GitHub Actions, Environments, Secrets and APIs, with no third-party secret manager.
- Status
- Source onlyv0.1.0 baseline merged; no GitHub release yet
- Ships as
- Repository template + CLIThe
gitforgeopsbinary, built by your own workflows - Built with
- Rust + GitHub ActionsEnvironments, Secrets and the GitHub API; age encryption
- Ferrum Edge
- v0.9.11 historical qualificationSeven hosted local-gateway scenarios passed. The pinned v0.9.15 pairing awaits lifecycle qualification; v0.9.17 is not qualified
8013a14716fd9eeffc0ad5f2447c12409d362ec8 baseline to
main as 74efb135da1557990f695f0cea92dbd0a68e608b. Hosted lifecycle run
37245424910
passed all seven runnable local-gateway scenarios using the published Edge v0.9.11 binary. This historical run does not qualify Edge v0.9.15 or v0.9.17.
(SHA-256 97cbd7cd277feee8f661e9d2cd97a6383be969fd2fc2b61143601614922f988f) and
contracts-edge-0.9.11 at 390edbd5b2485af0988e02f7827fde778d76ae0a.
Six external GitHub, disposable-repository, approval and fault-injection scenarios were explicitly
skipped. This qualifies that lifecycle only; it does not establish human GitHub acceptance, a first
release, or broader operational support. The first release remains unpublished and unaccepted, and
issue #266's
human release checklist remains required. The CLI, configuration and state formats may change without
an upgrade path. Once the release record reads supported, adopt only the source revision
and gateway pairing it names; see the
release notes.Gateway changes that go through the same review as code
Each proxy, consumer, upstream, plugin config and mesh fragment is a YAML file under
resources/<namespace>/. The directory names the namespace, and
overlays/<environment>/ holds the few keys that differ per environment.
- Open a PR: static validation for everyone, a live plan and review comment for trusted PRs
- Merge: each environment applies in its own job, after its required reviewer approves
- Scheduled drift checks: API environments compare the live gateway with the repository after approval, or through a separately configured monitor environment
kind: Proxy
spec:
id: "orders-proxy"
name: "Orders API"
listen_path: "/orders"
backend_scheme: https
backend_host: "orders.internal"
backend_port: 8080
strip_listen_path: true
tests/fixtures/quickstart/. A production overlay changes only backend_host.A Control Plane Made of Pull Requests
Review, approval, audit trail and secrets come from GitHub itself. GitForgeOps adds the gateway-aware parts.
Plans on every pull request
A review comment per environment shows validation, the Add / Modify / Delete changes, breaking changes, security findings, unmanaged resources and an apply verdict.
Many environments, one repository
Declare staging, production and more in .gitforgeops/config.yaml. Each deploys to its own gateway through its own GitHub Environment, with deep-merge overlays.
Ownership modes
shared (the default) touches only what the repository has applied and leaves hand-made changes alone. exclusive makes Git authoritative for named namespaces.
Policy rules
Eleven opt-in rules, from TLS-only backends to required authentication, WAF and AI guardrails. severity: error blocks apply unless a maintainer overrides it on the PR.
Credential broker
Resource files hold ${gh-env-secret:...} placeholders. Values are generated at apply time, kept in Environment Secrets and delivered age-encrypted to the PR author.
Ownership-aware drift detection
A daily gitforgeops diff per API environment. By default it waits for the deployment environment approval; unattended monitoring requires the documented separate monitor environment. Shared mode alerts on managed resources that changed or vanished, so unmanaged additions stay quiet.
Staged promotion
With promotion.requires: staging, production waits until staging has applied the same revision and passed its declared traffic checks.
Adopt a running gateway
gitforgeops import turns a live gateway or backup into a resource tree. Every secret becomes a placeholder; real values go to a separate private bundle.
File mode and mesh
For file-mode gateways, apply commits an assembled document with placeholders intact. MeshConfig fragments merge into one standalone mesh document. Fleet delivery and live convergence observation require an external system.
Approved, ordered, and safe to re-run
After merge, each environment applies in its own job, bound to its GitHub Environment, so its required reviewer, branch policy and secrets all apply. The person who merged cannot approve the apply.
- Namespace by namespace, in dependency order
- One apply per environment at a time; a run moves to the current head of
main - Refuses stale or superseded deployments instead of replaying old state
- Re-running a failed apply is safe: finished work is skipped
- The state-writer GitHub App commits the ownership ledger,
.state/<env>.json, back tomain
Triggering commit: 4f2c… Protected main HEAD: 4f2c… ... Allocated 1 credential slot(s): ferrum/orders-client/keyauth/key -> @you (ssh SHA256:…) Applied: 4 created, 0 updated, 0 deleted, 0 deletes deferred, 0 unmanaged skipped, 0 spec-owned skipped convergence: mode=cp, 1 data-plane node(s), 0 mesh node(s) connected; oldest last_sync_at … ... Pushed state on attempt 1.
docs/quickstart.md (§8).Consumer credentials that never touch the repository
Credentials are declared as placeholders. alloc=generate creates the value on the first
apply, stores it in the environment's FERRUM_CREDS_BUNDLE* secrets and posts it to the PR,
age-encrypted to the author's GitHub SSH key.
- A literal secret in resource YAML is a blocking security finding
alloc=require(the default) expects a value you seeded- Rotate a slot with the
rotate.ymlworkflow orgitforgeops rotate - Bundles shard automatically: roughly 7,000 slots per environment
kind: Consumer
spec:
id: "orders-client"
username: "orders-client"
credentials:
# The broker allocates this on the first apply and delivers it,
# age-encrypted, to the pull request's author. Never write a literal
# secret here: `apply` refuses the document before it reads a bundle.
#
# Slot: ferrum/orders-client/keyauth/key
keyauth:
- key: "${gh-env-secret:alloc=generate}"
age -d -i ~/.ssh/id_ed25519.Your standards, enforced in review
Every rule is off until you enable it. Violations name the rule, the resource, the current value and a
fix. error blocks apply; warning and info only report.
- Override on the PR only: a permitted account adds the override label and submits an override review on the current head
- A new push needs a new override review
- Overridden findings stay visible, marked
OVERRIDDEN by @user, and the ledger records the evidence - Validation, credential and ownership gates are never overridable
version: 1
policies:
backend_scheme:
enabled: true
severity: error # error | warning | info
allowed_protocols: [https, tcps, dtls]
require_auth_plugin:
enabled: true
severity: error
overrides:
require_label: gitforgeops/policy-override
required_permission: write
docs/policies.md. policies.example.yaml documents every key.| Rule | Checks |
|---|---|
proxy_timeout_bands | Proxy connect, read and write timeouts stay inside min/max bands |
backend_scheme | The effective backend_scheme is in allowed_protocols |
require_auth_plugin | Every proxy runs an enabled authenticator on every protocol it serves |
forbid_tls_verify_disabled | No proxy or upstream disables backend certificate verification |
allowed_proxy_plugins | Proxies use only allowlisted plugins |
allowed_backend_domains | Statically authored destinations match an allowlist |
waf_enforcement | Attached waf plugins actually block |
require_ai_guardrails | AI proxies carry an enforcing content guardrail |
rate_limit_completeness | Rate limiters have a usable budget |
plugin_name_is_known | plugin_name is a built-in or an allowlisted custom name |
priority_override_range | priority_override is within 0..=10000 |
From Pull Request to Running Gateway
GitForgeOps drives each gateway through the Ferrum Edge Admin API, the same interface Foundry uses. Nothing runs on the gateway itself.
resources/ and overlays/review, merge
apply · GET /backup
PR code never sees secrets
validate-pr.yml has a read-only token and no Environment. The live review runs the protected-branch binary on a sanitized copy of the PR's YAML. Fork PRs get static review only.
Validated by the gateway binary
Every assembled document is checked with ferrum-edge validate, using a validator build pinned by SHA-256 in .github/ferrum-edge-checksums.txt.
API mode or file mode
API mode reconciles a running gateway. File mode assembles a document you deliver yourself; set live_review: false for environments without an Admin API.
Bundled Workflows, and the Same Engine Locally
The template ships the GitHub Actions workflows; the gitforgeops CLI runs the same operations from your machine.
| Workflow | Trigger | Purpose |
|---|---|---|
validate-pr.yml | Pull request | Secretless static validation, the gitforgeops-required-static-validation check |
trusted-pr-review.yml | After PR validation | Environment-gated live review comment |
state-guard.yml | Pull request | Rejects PR edits to .state/ |
apply-on-merge.yml | Push to main | Apply per environment, then staged promotions |
drift-check.yml | Daily, manual | Drift report per environment |
rotate.yml | Manual | Rotate one consumer credential |
materialize-file.yml | Manual | Encrypted, fully resolved file for file-mode gateways |
settings-audit.yml | Weekly, manual | Checks GitHub protections against the launch baseline |
validator-pin-canary.yml | Daily, manual | Opens an issue when the validator digest pin is stale |
| Command | Does |
|---|---|
gitforgeops validate | Assembles the tree, runs the security audit and ferrum-edge validate |
gitforgeops plan | Shows what apply would do and exits non-zero for every reason it would refuse |
gitforgeops apply | Reconciles the gateway (API mode) or writes the assembled files (file mode) |
gitforgeops diff | Compares desired state with the live gateway; --exit-on-drift exits 2 on drift |
gitforgeops review | Renders, and with --pr posts, the PR review comment |
gitforgeops import | Turns a live gateway or backup file into a resource tree |
gitforgeops export | Writes the assembled document; --materialize --encrypt-to LOGIN resolves and encrypts it |
gitforgeops verify | Runs the environment's declared traffic checks |
gitforgeops rotate | Rotates one consumer credential slot |
gitforgeops doctor | Read-only readiness diagnosis |
gitforgeops envs / version | Lists environments for CI matrices / prints build metadata |
Is this repository ready to deploy, and what is still wrong?
Doctor changes nothing and prints no secret values. It checks the local tree, the GitHub settings against the launch baseline, and, when you opt in, the gateway itself.
- local: config, overlays, resource tree, policy file, validator binary and digest
- github: rulesets, App bypass, environments, labels and required checks
- gateway:
GET /healthandGET /clusterprove connectivity and JWT claims UNKNOWNis never counted as a pass; exit3means something is blocking
FAIL Configured overlays exist: missing overlay directories: staging -> overlays/staging -> Create the directory (an empty one is valid) or remove `overlay:` from that environment. UNKNOWN Repository settings match the launch baseline: no GH_TOKEN with Administration: read 4 passed, 2 failed, 0 warned, 1 unknown, 3 skipped.
GitHub-Native, with a Rust Engine
Your template copy builds the engine from its own checkout. Dependencies are pinned: Actions by commit SHA, Rust and tools by exact version, Docker bases by digest.
gitforgeops CLI, with clap, Tokio and serdegitforgeops and digest-pinned Edge v0.9.11; historical lifecycle qualification only, first release awaits human acceptanceQuick Start
One API-mode gateway, one namespace, one environment. The full quickstart walks through every step and what to expect.
-
Create your repository from the template
bashgh repo create acme/gateway-config \ --template ferrum-edge/ferrum-edge-git-forge-ops --privateUse a template copy, not a fork. Check the plan requirements below before choosing visibility. Once the release record is
supported, start from its exact source revision instead, using the adoption steps. -
Apply the launch controls
bashexport GH_TOKEN=$(gh auth token) python3 .github/scripts/bootstrap_repo_settings.py --repo OWNER/REPO \ --state-writer-app-id 123456 \ --environment production \ --reviewer SECOND-MAINTAINER --applyFirst create the state-writer GitHub App (Contents: read and write only) and set
GITFORGEOPS_STATE_APP_ID. The script writes rulesets, Actions policy, labels and environments; without--applyit only prints the plan. It never reads or prints a secret. -
Declare the environment
.gitforgeops/config.yamlversion: 1 environments: production: overlay: production apply_strategy: incremental ownership: mode: shared default_environment: productionGitHub Actions deploys nothing without this file. It never holds URLs, secret names or credentials, and the name must match the GitHub Environment.
-
Set the environment secrets
bashgh secret set FERRUM_GATEWAY_URL --repo OWNER/REPO --env production gh secret set FERRUM_ADMIN_JWT_SECRET --repo OWNER/REPO --env production gh secret set GITFORGEOPS_STATE_APP_PRIVATE_KEY --repo OWNER/REPO --env production \ < app-private-key.pem gh secret set FERRUM_GH_PROVISIONER_TOKEN --repo OWNER/REPO --env production gh secret set SETTINGS_AUDIT_TOKEN --repo OWNER/REPO --env settings-auditThe gateway URL must be
https://and the JWT secret must be the gateway's own signing secret. The broker createsFERRUM_CREDS_BUNDLE*itself. -
Add resources, check locally, open a pull request
bashcargo build ./target/debug/gitforgeops validate ./target/debug/gitforgeops --env production planPut files under
resources/ferrum/(see the examples above) and add an SSH key to your GitHub account so a generated credential can be delivered to you. Merge the PR, have your second maintainer approve the apply, then decrypt the delivered key. -
Confirm the loop is closed
bashgitforgeops --env production diff --exit-on-drift # exit 0: in sync
GitHub Plan Requirements
Three controls depend on repository visibility and plan, per GitHub's documentation as checked by the project on September 16, 2026. Plans change, so confirm with GitHub.
| Control | Public repository | Private or internal repository |
|---|---|---|
| Environments and environment secrets | All plans, including Free | Pro, Team or Enterprise; not Free |
| Deployment branch policy | All plans | Pro, Team or Enterprise |
| Required reviewers on environments | All plans | Enterprise only |
| Actions minutes | Free on standard hosted runners | Plan allowance, then billed |
A private repository with a required reviewer needs Enterprise. On Free, Pro or Team, either make the repository public (every control works, and your gateway configuration is public while credential values are not) or accept that a private repository on Pro or Team has no human approval before apply, which the settings audit reports.
Works With the Rest of the Stack
Licensing
Ferrum GitForgeOps is licensed under PolyForm Noncommercial 1.0.0 for non-commercial use; commercial licenses are available.