Skip to main content

Security and trust

Your instance, your data, your evidence. This page states what Modulo does, what leaves an instance, and what it does not claim.

Deployment model

Modulo is self-hosted only. There is no hosted service, and no Farnalabs-operated instance sits in the request path of a run. You run the application, the database, the audit chain and the model credentials on infrastructure you control: a cloud account you own, your own data centre, an air-gapped network, or a host reachable only through a VPN.

Nothing in that deployment model changes with the tier. The free Community tier and the paid Team tier run the same self-hosted product on your infrastructure. There is no phone-home, no network dependency on us for a run to start or finish, and no usage reporting to Farnalabs by default.

What leaves the instance and what does not

The honest answer has two parts: what the product does on its own, and what it does when an operator points it at something.

What does not leave

  • No telemetry by default. Run data, prompts, outputs, credentials and usage counters are not sent to Farnalabs.
  • No call-home licence check. An instance does not contact a licence server to start, to keep running, or to report headcount.
  • No product-driven outbound traffic when no external model backend, connector or notification endpoint is configured.

What leaves, by your configuration

  • Model calls go only to the model backends you configure. The prompt and the data it carries travel to that backend because that is the backend you selected.
  • Connectors send data to the endpoint they are bound to. A code host, an issue tracker or a document store receives the payload of the operation you asked for.
  • Notification endpoints receive what you route to them. A webhook or SMTP relay gets the alert payload it was configured for.

Each of those destinations is a decision the operator makes and controls. The instance does not add a destination of its own.

Identity and access

Single sign-on uses SAML 2.0 or OIDC against the identity provider you already run, so authentication stays under your directory's session and password policy. SCIM provisioning creates and deactivates accounts from that directory, so access ends when the directory says the person has left.

Team-scoped role-based access control separates what a viewer, a runner, an operator and an admin can do. Human-in-the-loop gates can be marked human-only, which means an automated agent cannot claim or approve its own gate: the decision is a person's action, and it is recorded as one.

Sign-ins, permission changes, approvals and configuration changes all land in the audit log.

Team-tier capability groups are: single sign-on and directory provisioning, team-scoped role-based access control, the audit viewer with its export and evidence pack, spend limits and cost controls, email and error-forwarding configuration, plugin management, run retention, schema version history, rate limits and runtime configuration. That is 18 features in total. The free Community tier is the same self-hosted product without them.

Evidence and auditability

The audit log is hash-chained. Every entry carries a SHA-256 hash over its own content and the hash of the entry before it, so altering or removing a record in the middle of the chain breaks every hash that follows. Tampering becomes detectable rather than merely discouraged.

Append-only is enforced at the database level. Triggers reject UPDATE and DELETE against the audit table, and the application layer refuses the same operations, so the guarantee does not rest on one code path behaving correctly.

Entries are scoped to an organisation: an instance with several organisations keeps a separate chain per organisation, and an organisation sees only its own.

An operator can pull the whole organisation chain through the export endpoint, and can build a signed evidence pack for a third party to verify offline with the published verification key. That means the evidence can be checked by someone who does not trust, and does not have access to, the running instance.

Release integrity

Each release produces a CycloneDX software bill of materials. The document is validated as CycloneDX and a component-less document fails the release rather than shipping, and the validated SBOM is published as an artefact of the release run.

Released file artefacts carry a SLSA build-provenance attestation, published through the repository's attestations API. The release smoke gate verifies provenance fail-closed, so an artefact without a verifiable attestation is refused before it is installed.

Release tarballs are signed with ed25519 and published with their SHA-256 checksums and the signed release manifest.

The installer verifies the ed25519 signature, the signed manifest and the SHA-256 checksums, and refuses to install when any of them fails. A substituted or modified tarball stops at the installer, not at first run.

Dependency and secret hygiene

bandit, gitleaks, semgrep and import-linter run in pre-commit, and all five checks below run again in CI, so they gate the build as well as the commit.

  • pip-audit checks Python dependencies against known advisories.
  • bandit scans the Python source for common security defects.
  • gitleaks blocks committed secrets before they reach the history.
  • semgrep enforces the project's own rules on tenancy, credential handling and query construction.
  • import-linter keeps the module boundaries that the design depends on.

Vulnerability disclosure

Report a vulnerability to [email protected], or use GitHub's private vulnerability reporting on the repository. Please do not open a public issue for a security defect.

The disclosure policy, the supported versions and what to include in a report are in SECURITY.md. Reports are acknowledged within 48 hours.

The machine-readable contact is published at /.well-known/security.txt.

Continuity

Modulo's source is public. It is licensed under the Business Source License 1.1, with Farna Labs Ltd as Licensor. The full text is in the LICENSE file.

The licence grants the right to copy, modify, create derivative works and redistribute the source, plus non-production use. Its Additional Use Grant permits personal, internal and commercial production use. The only restriction is that you may not use Modulo to offer a paid hosted or managed service to third parties that provides substantially the same functionality as Modulo.

You can fork Modulo, patch it and run your instance internally with no ongoing relationship with Farnalabs. Nothing in the licence requires our permission or a live connection to us to keep running.

Each released version converts to the Apache License 2.0 three years after that version's first public release. The licence calls that date the Change Date, and it is the mechanism by which the licence restrictions expire for a given release.

Team-tier features are unlocked by a signed licence key. The key is time-limited and renewed.

An absent or expired Team licence key does not stop the instance and does not endanger data. The instance keeps running on the Community tier: pipelines run, the audit chain keeps recording, and nothing is deleted. Team-tier capabilities switch off until a valid key is entered, and re-entering one restores them without a restart.

Security questionnaire answers

These are the answers to the questions that recur in a vendor security review. Each answer describes the self-hosted product as it ships, not a hosted service.

What is the deployment model, and is the product multi-tenant?
Self-hosted. You run the application, database and queue on infrastructure you choose, including an air-gapped network. Farnalabs does not host an instance and has no access to yours. One instance can serve several organisations, and each organisation is isolated by row-level security in Postgres. A single-organisation deployment is the common case.
Where is customer data stored, and in which region?
In your database, in the region and on the infrastructure you choose. Farnalabs receives no copy, so there is no Farnalabs region or data centre to declare. If you configure a third-party model backend, prompts travel to that backend and its residency terms apply to those calls.
What data does Farnalabs receive from a running instance?
Not by default. There is no telemetry, no usage reporting and no call-home licence check unless an operator turns it on. An operator can optionally enable aggregate product analytics, which posts usage counters (never run data, prompts, outputs or credentials) to a vendor endpoint they configure, and it requires both an instance-level switch and per-organisation consent. With it off, which is the default, the instance makes no outbound call to us. Run data, prompts, outputs and credentials stay in your instance. The licence key is verified locally against a public key, so activating or renewing Team does not make the instance contact us.
Which subprocessors do you use?
None for the self-hosted product. Farnalabs processes no data from your instance, and no Farnalabs subprocessor sits in the path of a run. Data leaves your instance only towards destinations you configure: model backends, connectors, notification endpoints and a hosted execution environment if you enable one. Each of those is your choice and your contract. The separate opt-in community library is the only service Farnalabs operates that processes submitted content, and its subprocessors are listed in its Data Processing Agreement.
Will you sign a Data Processing Agreement?
There is no DPA for instance data, because Farnalabs does not process it. You control the instance and the data in it. The opt-in community library is a separate service that does process submitted content, and its Data Processing Agreement covers that processing.
How is data encrypted in transit and at rest?
In transit, Modulo serves HTTP and expects you to terminate TLS at your reverse proxy, load balancer or ingress. Postgres connections support TLS through sslmode in DATABASE_URL, and the application rejects the prefer and allow values because they can silently downgrade to plaintext. Redis can use TLS through a rediss:// URL. At rest, connector credentials, model backend API keys, MCP server credentials, webhook and notification secrets, entries in the secrets table and agent checkpoints are encrypted in the database with Fernet (AES-128-CBC with HMAC-SHA256) using FERNET_KEY. The application refuses to start without SECRET_KEY and FERNET_KEY. Run inputs, run outputs and the audit chain are stored in your database without application-level encryption, so disk or volume encryption is your control.
Which authentication methods are supported?
Local accounts with passwords configured by the operator, OIDC or SAML 2.0 single sign-on against your identity provider, and SCIM 2.0 provisioning from that directory. SSO and SCIM are Team-tier features.
What authorisation model is used, and how is tenant data isolated?
Organisation roles: viewer, runner, operator and admin. A user's team role is capped by their organisation role. On Postgres, row-level security is enabled on every organisation-scoped table, and the application connects as a non-owner database role without BYPASSRLS. A small set of tables that exist before an organisation context (organisations and invitations), and system-administration paths, are outside RLS by design. An instance can hold several organisations with separate data. Team-scoped role-based access control is a Team-tier feature.
What does the audit log record, can it be altered, and how is it exported?
Sign-ins, permission changes, approvals, configuration changes, run starts, agent dispatches and gate outcomes are recorded. Every entry carries a SHA-256 hash over its own content and the hash of the entry before it, and the audit table is append-only: triggers reject UPDATE and DELETE. Each organisation has its own chain. The audit viewer, the export endpoint, the chain scan and the signed evidence pack for offline verification are Team-tier features. The paginated audit-list API, GET /api/v1/admin/audit, is not feature-gated, so an administrator on the Community tier can still read audit events over the API. Farnalabs holds no copy, and retention is whatever your database and backups keep.
How do you handle vulnerability reports?
Report to [email protected] or through GitHub's private vulnerability reporting. SECURITY.md sets out the policy, supported versions and what to include. Reports are acknowledged within 48 hours, triage follows within 7 days, and disclosure is coordinated on a 90-day window, or sooner when a fix is available. The machine-readable contact is published at /.well-known/security.txt. There is no bug bounty programme.
Which secure development practices run in CI?
bandit, semgrep and gitleaks run in pre-commit and again in CI. CI also runs pip-audit against Python dependencies, Trivy against dependency CVEs and misconfigurations, SonarCloud static analysis, and a pnpm audit gate at critical severity for frontend dependencies. OWASP ZAP DAST runs nightly against staging, and mutation testing runs weekly.
Which compliance attestations and certifications do you hold?
None. There is no SOC 2 report, no ISO 27001 certificate, no published penetration test report, no named Data Protection Officer and no uptime SLA. The running system is your instance, so an attestation over Farnalabs' environment would not describe the system your auditor assesses. The audit chain and its export are the evidence you can give an auditor.
What backup, disaster recovery and business continuity commitments apply?
These are your controls, because the instance is yours. Modulo ships backup and restore tooling: an encrypted archive script that dumps Postgres, collects the key material and encrypts the archive with AES-256-CBC, and a directory-based backup command that writes unencrypted output. Scheduling backups, encrypting anything written unencrypted, storing copies off-site and testing restores are operator responsibilities. Farnalabs holds no copy of your data and cannot restore it for you.
What incident response and notification commitments do you offer?
There is no formal incident response SLA and no contractual notification window for self-hosted customers. Security reports are acknowledged within 48 hours through the disclosure process, and the coordinated disclosure timeline in SECURITY.md applies to vulnerabilities in the software. Incidents inside your instance are yours to detect, contain and notify on, because the logs and the data are yours.

What these answers do not include

A procurement review should record these as absent, not infer them from the rest of the page.

  • No SOC 2 or ISO 27001 attestation.
  • No published penetration test report.
  • No named Data Protection Officer.
  • No uptime SLA and no contractual SLA.
  • No bug bounty programme.
  • No hosted or managed offering from Farnalabs.
  • No vendor-operated backup, disaster recovery or incident notification service.

Honest limits

Modulo holds no SOC 2 or ISO 27001 attestation, and we publish no audit report today.

Modulo is self-hosted software: the running system is your instance, on your infrastructure, operating under your controls. A certification would attest to controls over an environment that we do not operate for you. When an organisation's auditor needs assurance, that auditor audits the instance in front of them.

What we provide instead is the evidence chain and the export: a hash-chained, append-only record that can be pulled in full and verified outside the product. That is the substrate an auditor or a customer's security team works from.