Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
16 changes: 9 additions & 7 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -293,12 +293,14 @@ jobs:
# < 50ms") was already false for the SSVC pack this repo ships. The
# benchmark now holds the numbers and this job enforces them.
#
# Gating is on medians, and `--slack 1.5` multiplies every published target
# before comparing: this runner measured ~1.7x slower than a developer
# machine on the compilation case, so enforcing the published numbers here
# verbatim would fail on the hardware rather than on the code. A red bench is
# meant to mean "something got materially slower", never "the runner was
# busy".
# Gating is on medians, and `--slack 2.0` multiplies every published target
# before comparing: enforcing the published numbers on this hardware would
# fail on the runner rather than on the code. The factor is measured, not
# guessed. Five consecutive runs of this job put the single-rule median at
# 105, 118, 130, 166 and 166 microseconds against 89 on the developer
# machine the targets come from -- 1.2x to 1.9x. It was 1.5x here, which the
# two slowest of those runs walked straight through. A red bench is meant to
# mean "something got materially slower", never "the runner was busy".
bench:
needs: changes
if: github.event_name == 'push' || needs.changes.outputs.code == 'true'
Expand All @@ -316,4 +318,4 @@ jobs:
run: uv sync

- name: Benchmark
run: uv run python scripts/benchmark.py --slack 1.5
run: uv run python scripts/benchmark.py --slack 2.0
2 changes: 1 addition & 1 deletion .github/workflows/docs-deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ jobs:
version: 9

- name: Install Python deps
run: uv sync --extra docs --extra server --extra mcp
run: uv sync --extra docs --extra server --extra mcp --extra attestation

- name: Generate everything
run: |
Expand Down
37 changes: 29 additions & 8 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,16 @@ name: docs
#
# docs REQUIRED (branch protection). Deterministic and fast:
# version-sync + strict mkdocs build + docstring/SDK
# coverage + tutorial snippets. It does NOT regenerate
# artifacts, walk git history, or hit the network — all of
# which are nondeterministic across the squash-merge
# boundary or depend on an exact toolchain, and so must
# never gate a merge.
# coverage + tutorial snippets, plus source drift for the
# sources THIS pull request touched. It does NOT regenerate
# artifacts or hit the network — nondeterministic across the
# squash-merge boundary or dependent on an exact toolchain,
# and so must never gate a merge. The drift check is scoped
# to the branch's own edits for the same reason: a source
# edit ages every page citing it, and a whole-repo gate would
# fail the next unrelated pull request instead of the one
# that caused the drift. That whole-repo sweep is advisory,
# in docs-quality.
#
# docs-quality ADVISORY (not a required check; every step is
# continue-on-error). Regenerates artifacts and runs the
Expand All @@ -35,15 +40,19 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
# full history: the scoped drift check diffs against the PR base and
# reads each cited source's last meaningful commit.
- uses: actions/checkout@v7
with:
fetch-depth: 0

- name: Install uv
uses: astral-sh/setup-uv@v7
with:
python-version: "3.13"

- name: Install Python deps
run: uv sync --extra docs --extra server --extra mcp
run: uv sync --extra docs --extra server --extra mcp --extra attestation

- name: Version sync
run: uv run python scripts/check_version_sync.py
Expand All @@ -55,6 +64,15 @@ jobs:
- name: Build (strict)
run: uv run mkdocs build --strict

# Every page citing a source this branch edited must have been
# re-verified in this branch. Pages citing sources the branch did not
# touch are somebody else's drift and are left to the advisory sweep.
- name: Source drift (sources this branch touched)
if: github.event_name == 'pull_request'
env:
BASE_SHA: ${{ github.event.pull_request.base.sha }}
run: uv run python scripts/check_doc_sources.py --changed-vs "$BASE_SHA"

- name: Docstring coverage
run: uv run python scripts/check_docstrings.py

Expand All @@ -81,7 +99,7 @@ jobs:
python-version: "3.13"

- name: Install Python deps
run: uv sync --extra docs --extra server --extra mcp
run: uv sync --extra docs --extra server --extra mcp --extra attestation

# arduino/setup-protoc@v3 is unreliable (see ci.yml); install from the
# upstream release archive. continue-on-error: a missing foreign
Expand Down Expand Up @@ -132,7 +150,10 @@ jobs:
echo "::warning::Generated docs artifacts are stale. Run 'make docs-gen && make docs-gen-foreign' and commit."
fi

- name: Source drift (advisory)
# Whole-repo sweep: what has gone stale anywhere, including pages no
# open branch is touching. The blocking, branch-scoped check is in the
# required `docs` job.
- name: Source drift sweep (advisory)
continue-on-error: true
run: uv run python scripts/check_doc_sources.py

Expand Down
8 changes: 6 additions & 2 deletions .github/workflows/release-please-docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ jobs:
python-version: "3.13"

- name: Install Python deps
run: uv sync --extra docs --extra server --extra mcp
run: uv sync --extra docs --extra server --extra mcp --extra attestation

# Same protoc install pattern as docs.yml (arduino/setup-protoc@v3
# is unreliable, see comment there).
Expand Down Expand Up @@ -87,5 +87,9 @@ jobs:
git config user.name "release-bot"
git config user.email "release-bot@users.noreply.github.com"
git add -A
git commit -m "chore(release): regenerate docs artifacts for version bump"
# -s: the DCO app checks every commit on the release PR, and
# this one is authored by release-bot. Signing off as that
# same identity satisfies it; without this the release PR
# needs a hand-written third-party remediation commit.
git commit -s -m "chore(release): regenerate docs artifacts for version bump"
git push
6 changes: 6 additions & 0 deletions .github/workflows/release-please.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ name: release-please
# GITHUB_TOKEN cannot be used — PRs and tags it creates do not trigger
# other workflows (CI would never run on the release PR, and the tag
# push would never trigger pypi-publish).
#
# release-please authors its commits as the token's owner, so the
# `signoff` entry in release-please-config.json names that account. The
# DCO app matches a Signed-off-by line against the commit author; if the
# token is ever reissued to a different account, update that string too
# or every release PR comes up red.

on:
push:
Expand Down
28 changes: 28 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,34 @@ Use the [feature request template](https://github.com/KrakenNet/fathom/issues/ne
4. Update documentation if you're changing public API
5. Open a pull request

### Changing a public surface

`VERSIONING.md` lists what this project promises to keep working, and
`tests/test_public_surface.py` fails when that list and `fathom.__all__` drift
apart. Adding a name to `__all__` is a commitment, not a convenience — update
`VERSIONING.md` in the same change, and say in the changelog what a caller has
to do if you changed or removed one.

### Documentation that cites your code

Every page under `docs/` lists the files it describes in its frontmatter
`sources:`, with a `last_verified:` date. If your branch edits a file some page
cites, the required `docs` job fails until that page is re-verified **in the
same branch**: read the claims it makes about your file, correct the ones your
change made false, and set `last_verified:` to today.

```bash
uv run python scripts/check_doc_sources.py --changed-vs origin/main # what CI gates
uv run python scripts/check_doc_sources.py # everything stale, anywhere
```

Only the sources your branch touched are gated. The whole-repo sweep is a
warning in the advisory `docs-quality` job — it is a maintenance list, not
your problem to clear.

Line numbers in a citation (`src/fathom/engine.py` line 1179) move when the
file does; re-check them rather than only bumping the date.

### Sign-off (DCO)

Every commit must be signed off, certifying you wrote the change (or have the right to submit it) under the project license. Add the sign-off line by committing with `-s`:
Expand Down
40 changes: 32 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,24 +93,37 @@ See the [Getting Started guide](docs/getting-started.md) for a full walkthrough.
- **FastAPI REST server** with bearer-token auth and rule-path jailing
- **gRPC server** with bearer-token auth (see `protos/fathom.proto`)
- **MCP tool server** (`FathomMCPServer`) for agent discovery
- **LangChain adapter** callback handler
- **Framework adapters** — LangChain callback handler, CrewAI before-tool-call
hook, OpenAI Agents SDK tool guardrail, Google ADK before-tool callback.
Each is allowlist-only: the call proceeds when the decision is exactly
`allow`, and every other outcome raises `PolicyViolation` (ADK returns an
error dict instead)
- **CLI** — `fathom validate`, `fathom compile`, `fathom test`, `fathom bench`, `fathom info`, `fathom status`, `fathom verify-artifact`, `fathom verify-chain`, `fathom repl`
- **Docker sidecar** (Debian slim + uv)
- **Prometheus metrics** export (`/metrics` endpoint)
- **Policy Studio** — FastAPI + HTMX UI that mounts the REST engine in-process under `/api` (`python -m fathom.studio.app`)
- **Policy Studio** — browser UI over a real engine, shipped as its own
package (`packages/fathom-studio/`, run with `uv run fathom-studio`). See
[Running Policy Studio](docs/how-to/policy-studio.md)

**Rule packs**

- `fathom-owasp-agentic` — OWASP Agentic Top 10 mitigations
- `fathom-nist-800-53` — Access control, audit, information flow
- `fathom-hipaa` — PHI handling, minimum necessary, breach triggers
- `fathom-cmmc` — CMMC Level 2+ controls
- `fathom-ssvc` — SSVC supplier, deployer, and CISA vulnerability-triage trees (144 rules)

**SDKs (in progress)**

- `fathom-go` — REST + gRPC client (`packages/fathom-go/`)
- `fathom-ts` — `@fathom-rules/sdk` (`packages/fathom-ts/`); hand-written client, 4 of 10 REST endpoints
- `fathom-editor` — React visual rule editor (`packages/fathom-editor/`); stub
- `fathom-go` — REST + gRPC client (`packages/fathom-go/`); unit and
integration suites run in CI, not yet published to a Go proxy
- `fathom-ts` — `@fathom-rules/sdk` (`packages/fathom-ts/`); hand-written
client covering 4 of the 10 REST endpoints, vitest suite required in CI,
not yet published to npm

Scaffolds that are **not** usable yet — including the `fathom-editor` visual
rule editor — are catalogued in
[Planned Integrations](docs/reference/planned-integrations.md).

## Core Primitives

Expand Down Expand Up @@ -175,6 +188,7 @@ Entry points:
- [How-to Guides](docs/how-to/index.md)
- [Concepts](docs/concepts/index.md)
- [Reference](docs/reference/index.md)
- [Configuration](docs/reference/configuration.md) — every `FATHOM_*` variable and the gRPC TLS setup

## Performance Targets

Expand All @@ -189,8 +203,10 @@ Measured by `scripts/benchmark.py` and enforced on every pull request by CI's
`bench` job, which fails the build if a median regresses past its target.
Compilation is stated per rule because it scales with pack size: the packaged
SSVC pack is 144 rules. The numbers above are what the benchmark reports on a
developer machine; CI enforces them with a 1.5x allowance (`--slack 1.5`)
because a shared runner is roughly that much slower.
developer machine; CI enforces them with a 2x allowance (`--slack 2.0`)
because GitHub's shared runners measured 1.2x to 1.9x slower than that machine
across five consecutive runs of the same job. Run `python scripts/benchmark.py`
with no slack to hold your own hardware to the published numbers directly.

## Related Projects

Expand All @@ -205,7 +221,7 @@ git clone https://github.com/KrakenNet/fathom.git
cd fathom
uv sync --all-extras # --all-extras is required for the full test suite

uv run pytest # 1695 tests
uv run pytest # engine, integrations, and Studio suites
uv run ruff check src/ tests/ # lint
uv run mypy src/ # type check
uv run pytest --cov=fathom # coverage report
Expand All @@ -220,6 +236,14 @@ uv run uvicorn fathom.integrations.rest:app --reload

See [CONTRIBUTING.md](CONTRIBUTING.md) for full development guidelines and [CHANGELOG.md](CHANGELOG.md) for release notes.

## Stability

Fathom is pre-1.0. [VERSIONING.md](VERSIONING.md) names the surfaces that are
covered — `fathom.__all__`, the YAML authoring keys, the REST/gRPC/MCP
contracts, and the CLI — states what a `0.x` minor and patch bump each mean for
them, and defines the deprecation period. The symbol list there is checked
against the package on every test run.

## Star History

<a href="https://star-history.com/#KrakenNet/fathom&Date">
Expand Down
109 changes: 101 additions & 8 deletions SECURITY.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,108 @@
# Security Policy

## Supported Versions
## Supported versions

| Version | Supported |
| ------- | ------------------ |
| 0.7.x | :white_check_mark: |
The latest released minor is supported. Security fixes ship as a new patch of
that minor and are not backported: pre-1.0, upgrading to the current minor is
the supported response to a fix. See
[VERSIONING.md](VERSIONING.md#supported-versions) for the full policy and what
a version bump means for compatibility.

## Reporting a Vulnerability
Check what is current on [PyPI](https://pypi.org/project/fathom-rules/) or in
[CHANGELOG.md](CHANGELOG.md). Every release from 0.5.0 onward is signed; see
[Verifying a signed release](docs/how-to/release-signing.md).

Use GitHub's private vulnerability reporting: Security tab → Advisories → Report a vulnerability
## Reporting a vulnerability

### Response Timeline
Report privately through GitHub: **Security → Advisories → Report a
vulnerability**, or directly at
<https://github.com/KrakenNet/fathom/security/advisories/new>.

Acknowledge 48h, assess 7 days, fix critical within 30 days
Do not open a public issue, a pull request, or a discussion for a suspected
vulnerability. A public report starts the disclosure clock before there is a
fix to disclose.

Useful in a report: the version, the surface (library, REST, gRPC, MCP,
Studio), what an attacker gains, and the smallest input that reproduces it. A
ruleset or fact set that triggers the behaviour is worth more than a
description of it.

### What happens next

| Step | Target |
|---|---|
| Acknowledgement that the report was received | 2 business days |
| Assessment: in scope or not, and a severity | 7 days |
| Fix released for a critical or high finding | 30 days from assessment |
| Fix released for a moderate or low finding | Next scheduled minor |

If a fix will take longer than 30 days, the advisory thread says so and why,
rather than going quiet.

### Disclosure

Fixes are coordinated: the advisory stays private until a fixed version is
published, then it is published as a GitHub Security Advisory with a CVE
requested through GitHub's CNA. The advisory names the affected versions, the
fixed version, and the reporter — tell us if you would rather not be credited,
or how you want to be named.

The embargo runs until the fixed release is out, or **90 days** from
acknowledgement, whichever comes first. If a report is still unfixed at 90
days, the reporter is free to disclose; we would rather publish an advisory
with a workaround than let a real issue sit unannounced. A vulnerability
already public, or under active exploitation, is not embargoed — it gets an
advisory as soon as it is confirmed, with whatever mitigation exists.

## Scope

### In scope

- **The library** (`fathom` package) — including compiler injection: any
input to the YAML authoring surface that produces CLIPS constructs the
author did not write.
- **The REST server** (`fathom.integrations.rest`) — authentication, the
ruleset path jail, request size limits, and the hot-reload signature check.
- **The gRPC server** (`fathom.integrations.grpc_server`) — the same, plus TLS
configuration.
- **The MCP tool server** (`fathom.integrations.mcp_server`).
- **Hot reload** (`POST /v1/rules/reload` and the `Reload` RPC) — anything that
loads an unsigned or attacker-supplied ruleset where the configuration says
it should not.
- **Attestation and the audit log** — signature forgery, token replay across
sessions or inputs, and any way to alter a hash-chained log without
detection.
- **The release path** — the published wheels and their signatures, the
packaged public keys, and the workflows that produce them.
- **Policy Studio** (`packages/fathom-studio`) — authentication bypass, and
any path that lets a Studio request reach a file or a ruleset outside its
configured root.

### Not in scope

- **The `test:` conditional element and `type: raw` functions.** Both emit
author-written CLIPS verbatim and are documented escape hatches. A ruleset
author is trusted with the CLIPS environment; if untrusted parties author
rulesets in your deployment, the boundary you need is the signature check on
reload, not these.
- **`FATHOM_GRPC_ALLOW_INSECURE=1` and the unsigned-ruleset dev escape.** Both
require an explicit opt-in and both log what they disabled. Reports that
they are insecure describe their purpose.
- **Denial of service from a ruleset you deployed yourself.** Evaluation is
bounded by an activation budget and request bodies are capped; a ruleset
that is merely slow is a performance issue.
- **Missing hardening with no attack behind it** — a header that could be set,
a dependency that could be newer. Send a pull request or open an issue.
- **Findings in a dependency** with no Fathom-specific path to reach them.
Report those upstream; if Fathom's use makes an upstream issue exploitable
when it otherwise would not be, that is in scope and worth saying.
- **The visual editor** (`packages/fathom-editor`), which is a stub and is not
shipped.

## Hardening the deployment

Configuration that materially changes the attack surface — token scopes, the
ruleset path jail, request caps, gRPC TLS — is documented in
[Configuration](docs/reference/configuration.md). The reload security model,
including signature verification and key rotation, is in
[Hot-reloading rulesets](docs/how-to/hot-reload.md).
Loading
Loading