Contributing
Farrier is MIT-licensed and contributions are welcome: connectors for more devices, fixes, docs and UI. This page covers how the repository is laid out and what a good pull request looks like.
Repository layout
farrier/ Python engine (package "farrier")
├── api.py FastAPI app: REST API + background tick loop
├── cli.py `farrier` command
├── engine.py renewal state machine (plan / advance / tick)
├── policy.py CA/B Forum schedule + renewal timing maths
├── scheduler.py change windows and daily-cap leveling
├── discovery.py TLS scanning and target parsing
├── db.py SQLite store
├── vault.py key sealing and secret references
├── issuers/ ACME (RFC 8555, DNS-01) and the local CA
├── connectors/ files, keystore, f5, fortigate, panos, manual
└── demo.py simulated estate for `farrier demo`
tests/ pytest suite
examples/ inventory and DNS hook examples
web/ Next.js dashboard (what teams install)
└── src/
├── app/dashboard/ dashboard screens
├── app/api/farrier/ server-side proxy to the Farrier API (+ demo mode)
├── components/ui/ primitives: Button, Card, Table, Dialog…
├── components/ dashboard, charts, status vocabulary
├── lib/ API client, types, policy port, demo data
└── proxy.ts sign-in gate for the dashboard
site/ Next.js public website, a static export (landing page, these docs)
└── src/
├── app/page.tsx landing page
├── app/docs/ these docs (MDX)
└── components/ landing, site header and footer, docs chromeDevelopment setup
Engine
python -m venv .venv && source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -e ".[dev]"
farrier demoDashboard, in a second terminal:
cd web
npm install
echo "FARRIER_API_URL=http://127.0.0.1:8740" > .env.local
npm run devLeave FARRIER_API_URL unset to work against the built-in demo data.
Running checks
python -m pytest # engine: unit, end-to-end, ACME and connector tests
cd web && npm run lint && npm run buildThe engine tests need no network. ACME runs against an in-process RFC 8555 server, and appliance connectors run against mock management APIs (httpx.MockTransport). npm run build also type-checks and prerenders the docs.
Conventions
Python
The engine is grouped by responsibility:
farrier/
api/ REST API, dashboard configuration routes, MCP server
core/ renewal engine, policy, change windows, discovery, X.509
db/ store, SQL dialects, migrations, leader election, backups
security/ vault, secret store and backends, cloud identity, KMS
config/ engine settings, dashboard-managed integrations
monitoring/ health checks, zombie scanner, alert rules, notifications
events/ CloudEvents and their publishers
connectors/ F5, FortiGate, PAN-OS, keystores, files, change packs
dns/ DNS-01 providers and accounts
issuers/ ACME and the private CA
storage/ object storage (file, S3, GCS, Azure Blob)- Python 3.11+, type hints,
from __future__ import annotations. - Keep dependencies minimal:
cryptography,httpx,fastapi,uvicorn. - Engine changes need a test. State-machine changes need an end-to-end test in
tests/test_engine.py. - Never log or return secrets or private keys.
TypeScript / web
- Components use design tokens, never raw hex. Read the Design system first.
- Data fetching goes through the hooks in
web/src/lib/api.ts. The browser only talks to/api/farrier/*. web/src/lib/types.tsmirrors the API infarrier/api/. If you change an API response, update the types in the same PR, and updateweb/src/lib/demo.tsso demo mode stays faithful.web/src/lib/policy.tsis a port offarrier/core/policy.py. Change both together.- Docs live in
site/src/app/docs/<slug>/page.mdx(preview withnpm run devinsite/, port 3100). Document every config key, route and state you add.
Adding a connector
- Write the class in
farrier/connectors/<kind>.pyfollowing Writing a connector, with a module docstring listing its config keys (the API serves it to the dashboard). - Register it in
REGISTRYinfarrier/connectors/__init__.py. - Add tests against a mock transport in
tests/test_appliances.py: deploy, rollback, and failure mid-deploy. - Add a label to
CONNECTOR_LABELinweb/src/components/status.tsx, and a section toconnectors/page.mdx. - Say in the PR whether you tested against real hardware, and which firmware version.
Pull request checklist
-
python -m pytestpasses -
npm run lintandnpm run buildpass inweb/ - New behaviour has tests
- API changes are reflected in
web/src/lib/types.ts, the demo data and the API reference - New config keys and defaults are documented
- UI checked in light and dark mode, at mobile width, and with keyboard only
- No secrets, keys or customer hostnames in code, tests or screenshots
Reporting security issues
Don't open a public issue. Follow the private process on the Security page.