An open-source ontology platform for building operational digital twins.
Define your domain once, in a schema. Open Foundry compiles it into a governed system: GraphQL and REST APIs, an authorization model, a transactional action pipeline, version history, and an audit trail — with the enforcement built in rather than bolted on.
The platform is domain-neutral. It ships no business concepts of its own; everything domain-specific arrives through composable domain packs.
Maturity: pre-1.0. Well tested and CI-gated, suitable for evaluation, prototyping and controlled pilots. It has not had an independent security audit or regulatory validation — see
SECURITY.mdbefore using it with sensitive data.
You declare an object type and an action that may change it:
type Book @objectType {
id: ID! @primary
title: String! @indexed @searchable(weight: 2.0)
status: BookStatus!
borrower: Member @link(type: "BorrowedBy", direction: OUTBOUND)
}
type BorrowedBy @linkType(from: "Book", to: "Member", cardinality: MANY_TO_ONE) {
id: ID! @primary
borrowedAt: DateTime!
}
type BorrowBook @actionType(permission: "can_borrow") {
book: Book! @param
member: Member! @param
}…plus a manifest saying when the action is allowed and what it does:
action: BorrowBook
preconditions:
- expr: "book.status == 'AVAILABLE'"
error: "That book is not available to borrow"
effects:
- type: updateObject
target: "book"
set: { status: "ON_LOAN" }
- type: createLink
linkType: "BorrowedBy"
from: "book"
to: "member"From that, the platform generates the API surface and governs every write:
curl -X POST localhost:4000/api/v1/actions/BorrowBook \
-H 'content-type: application/json' \
-d '{"book":"<id>","member":"<id>"}'
# → success, book UPDATED, BorrowedBy CREATED
curl -X POST localhost:4000/api/v1/actions/BorrowBook ... # again
# → PRECONDITION_FAILED: That book is not available to borrowThe book is now queryable over REST and GraphQL, its version history is at
/api/v1/books/<id>/history, and the change emitted an event. There is no
generic create/update/delete path — every mutation goes through an action,
which is what makes preconditions, authorization and audit unavoidable.
That example is real and runnable: it is
examples/library-pack/.
| If you want to… | Go to |
|---|---|
| Model your own domain | Build your first domain pack — a complete worked example in ~20 minutes |
| Evaluate the platform | Run the stack below, then the deployment guide |
| Contribute | CONTRIBUTING.md — setup, conventions, and the enforcement test suites |
cd deploy
cp .env.example .env # set POSTGRES_PASSWORD and KEYCLOAK_ADMIN_PASSWORD
docker compose up -d --wait
./init-services.shGraphQL playground at http://localhost:4000/graphql, REST at
http://localhost:4000/api/v1/.
Important
The default stack runs with governance enforcement disabled. It sets
NODE_ENV=development, which replaces OpenFGA and CEL with allow-all stubs and
gives unauthenticated requests a synthetic admin user holding every role. It is
for local iteration only — never expose it to an untrusted network.
Nothing you observe in this mode demonstrates authorization, consent or redaction actually working. To exercise real enforcement, run in production mode — see development vs production.
- Model a domain once. ODL is GraphQL SDL plus semantic directives. One schema defines object types, links, actions and permissions.
- Generated API surface. GraphQL (queries, mutations, subscriptions, filtering, aggregation) and REST, both derived from the schema, with OpenAPI, GraphQL SDL and AsyncAPI contracts published on every release.
- Every write is a governed action. Validate → authorize → consent → preconditions → execute in one transaction → side effects → audit → emit. Compensating transactions undo committed effects when a side effect fails.
- Relationship-based authorization. OpenFGA models derived from your schema, checked per object; field-level redaction driven by per-role field policies.
- History, audit and provenance. Version history and temporal queries on every object; an append-only audit record for every governed action.
- Connect existing systems. JDBC connectors and Debezium CDC, with overlay mode, conflict resolution and reconciliation.
- Swap the storage. Everything persists through a storage SPI; PostgreSQL + Apache AGE for production, in-memory for tests, both passing the same conformance suite.
- Standard operational surface. OpenTelemetry traces, Prometheus metrics, structured logs, a Helm chart with HPA, PDBs and network policies, and non-root containers.
Full detail lives in the specification; what is in each package is in the repository map.
+---------------------------------------------------------+
| Query & API Layer |
| (GraphQL, REST, FHIR R4, SDKs) |
+---------------------------------------------------------+
| Action Framework |
| (action types, CEL execution, side-effects) |
+------------------------+--------------------------------+
| Security Layer | Sync Engine |
| (OpenFGA ReBAC, | (JDBC connectors, Debezium |
| consent, audit) | CDC, conflict resolution) |
+------------------------+--------------------------------+
| Ontology Engine |
| (schema registry, object store, relationship index) |
+---------------------------------------------------------+
| Storage Provider Interface |
| (PostgreSQL+AGE | Memory) |
+---------------------------------------------------------+
Each layer talks only to adjacent layers through defined interfaces. Runtime writes go through the action pipeline; the one deliberate exception is bootstrap seed data, which is applied at boot by the object and link managers and therefore skips authorization, consent and audit.
A domain pack is a directory of schema, action manifests, permissions and seed data. Packs compose: a deployment loads only what it declares, and the platform adds nothing of its own.
| Pack | Namespace | Models |
|---|---|---|
| AML | aml |
Customer, Transaction, Alert, Case, Account, SuspiciousActivityReport |
| Supply Chain | supply.chain |
Product, Supplier, Shipment, Facility, InventoryRecord, PurchaseOrder |
| NHS Acute | nhs.acute |
Patient, Ward, Bed, Consultant, DischargeRecord, Transfer, Staff |
| Library (example) | example.library |
Book, Member — the tutorial's worked example |
Packs may live outside this repository and be mounted at deploy time; see external domain packs.
Two facades are capability-gated and off unless a loaded pack opts in: FHIR
R4 (/fhir/*) and a common-data-model projection (/api/v1/cdm/*). The shipped
CDM profile targets the NHS Federated Data Platform — that vertical's design
notes, integration plan and mapping profile are in docs/nhs/.
The platform fails closed: production boot validates required configuration and the authorization model, and exits rather than starting without them.
Two things worth knowing before you rely on the defaults:
- Field redaction is policy-driven. Marking a field
@sensitiverecords intent; redaction engages for an object type only once apermissions/field-permissions.yamllists it — after which every unlisted field is hidden, link fields included. - Audit covers governed actions, plus relationship grants and consent records. Bootstrap seeds run outside that pipeline and are not audited.
Supply chain: releases carry a CycloneDX SBOM and a build provenance
attestation; GitHub Actions are pinned to commit SHAs; CI fails on
HIGH/CRITICAL container vulnerabilities and a scheduled scan tracks drift.
Details and reporting process in SECURITY.md.
Semantic versioning. While pre-1.0, minor versions may contain breaking changes
— always called out under Breaking changes in
CHANGELOG.md.
Depend on a tagged release, not main. Each release publishes container
images and a versioned Helm chart to GHCR, and attaches its OpenAPI, GraphQL SDL
and AsyncAPI contracts, a CycloneDX SBOM, per-image digests and a build
provenance attestation. Chart and image versions always match the release, so an
upgrade is reproducible. Only the most recent release receives security fixes
while pre-1.0. See installing a released version.
| Item | Description |
|---|---|
| Backup and restore | BackupCapability is specified but not yet implemented by a storage provider |
| Performance baselines | Published benchmarks and a regression gate |
| Schema registry persistence | PostgreSQL-backed registry shipped; git-backed storage pending |
| FHIR write operations | Mutation support (currently read-only) |
| Application framework | Embeddable UI components for common ontology operations |
| Federation protocol | Multi-instance synchronisation across organisational boundaries (spec-only today) |
| Additional storage providers | TypeDB, Neo4j, and other graph-capable backends |
Start at docs/ for the full index.
| Document | Description |
|---|---|
| Build your first domain pack | Tutorial — a complete worked example |
docs/external-domain-packs.md |
Domain-pack reference |
docs/open-foundry-spec-v2.md |
Full technical specification |
docs/api-spec.md |
API contract artifacts and codegen |
docs/repository-map.md |
Packages, storage providers, test layers |
deploy/README.md |
Deployment, production mode, operational footguns |
docs/nhs/ |
NHS vertical — reference implementation |
CONTRIBUTING.md · SECURITY.md · CHANGELOG.md |
Contributing, security policy, release history |
Open source · composable · storage-agnostic · standards-native · federation-first · schema-driven · observable. Each layer is independently replaceable through a defined interface.
Open Foundry was scaffolded by an agent pipeline and hardened through human-led review — see project history.
Apache 2.0