FORGE-200 — The Forge Runtime
Project Forge — First Execution Document
Status: Normative for execution. Implements FORGE-100 and FORGE-101; introduced by Capability 7A.
Precedence: the constitutional corpus (FORGE-001 … FORGE-103) governs. This document does not amend the constitution; it explains and binds how the constitution executes. Where a reading of this document contradicts FORGE-101, FORGE-101 governs.
Historical note (Capability 7C, FORGE-202): this document was written while the runtime’s first organism, Mjolnir, was still declared in this repository at instances/mjolnir. FORGE-201 records why and how that organism moved into its own repository, forge-mjolnir; FORGE-202 records the completed engine-side cleanup. Every mechanism described below is unchanged by the move — the runtime never knew Mjolnir’s name — only the path in Part 2’s step 1 (instances/mjolnir) and the publication path in Part 5’s example (public/mjolnir/…) now name a directory that lives in Mjolnir’s repository, not this one.
Preamble
The constitutional corpus is complete: FORGE-101 defined the three planes — Core, Domain, Instance — and declared that Forge is a runtime, not a framework. What remained undefined was the machinery itself: the concrete thing that takes one instance’s declaration, composes it with the engine and its domains, and produces a living organism.
This document is the canonical explanation of how Forge executes. It documents the Forge Runtime as implemented in forge-core/runtime and the contracts it composes against — proven, at the time of writing, by composing the first Forge Instance, Mjolnir, from inside this repository. Mjolnir has since moved to its own repository (FORGE-201); the runtime’s mechanics below are exactly as they were, because nothing in them ever named it.
One sentence carries the whole document:
Project Forge is the engine; a Forge Instance is a living organism; the runtime is the act of composing the second out of the first — and the engine never learns the organism’s name.
And one principle governs how the engine is allowed to grow:
Adding a capability means adding a manifest, never editing the engine.
Every rule in this document exists in service of one or the other of these two sentences.
Part 1 — Engine vs Instance
The engine is everything under forge-core and the domain packages (forge-hpc, and its successors). It is the machinery of truth-keeping, publication, and presentation, plus the expertise of each scientific practice. The engine is reused by every deployment and owned by the Forge project and its domain communities.
An instance is one real deployment: Mjolnir today; Rubus, BioHPC, or another university’s cluster tomorrow. An instance is pure declaration (FORGE-101 Part 5.2): one forge-instance.json manifest carrying identity, branding, enabled capabilities, domain settings, and runtime options — and no logic. Every line an instance contains is a line the next adopter cannot reuse, so the architecture succeeds exactly insofar as instances approach pure declaration.
The prohibition that makes this real, restated from FORGE-101 Part 2.3 and now enforced by test:
The name of any instance appearing anywhere in engine code is a build failure. The engine knows that instances exist; it never knows which.
Mjolnir is therefore not “the product with a config file.” Mjolnir is a document the engine reads. Deleting instances/mjolnir leaves the engine whole; deleting any engine capability leaves Mjolnir a smaller organism, not a broken one.
Mjolnir is also not an analytics platform with a runtime bolted on. Analytics — like Queue Insights, Scheduler, or Storage — is one capability an organism may enable among many; it is not the organism’s identity. An instance’s wordmark names the organism, not the capability that happened to come first.
Part 2 — The Runtime
The Forge Runtime (forge-core/runtime) is the composition machinery of FORGE-101 Part 7.4, implemented. Its execution sequence:
1. LOAD the instance manifest (forge-instance.json)
2. VALIDATE identity: required fields, types, unknown keys fail loudly
3. DISCOVER capability registrations (forge-capability.json) under the
composition's declared roots — declaratively, at composition
time; no runtime plugin scanning
4. RESOLVE the instance's enabled capability ids against the registry;
unknown ids and undeclared domains fail composition
5. DERIVE navigation and the runtime identity document as pure
functions of (identity × resolved registrations)
6. PUBLISH the identity document into the instance's publication
namespace: public/<namespace>/identity.json, stamping the
moment of writing as the document's generation timestamp
Steps 1–4 are composition: pure validation, failures cheap and loud, no truth touched. Steps 5–6 are projection: deterministic, regenerable, drift-detectable — with one deliberate exception. The identity content DERIVE produces (Part 4) is a pure function of (identity × resolved registrations): re-deriving it from an unchanged declaration is byte-identical, forever. PUBLISH then wraps that content in a thin envelope recording when the wrapping happened. Only the envelope varies between runs; the test suite checks that everything else does not, and that a published document’s envelope is well-formed even when its timestamp cannot be reproduced by definition.
The runtime is a publication runtime (FORGE-101 Part 1.1): it runs at build time and emits static artifacts. Nothing in this document introduces a request-serving process.
Part 3 — The Organism Lifecycle
This is the canonical mental model for how Forge executes, from cold start to a serving organism:
Forge Runtime
↓
Load Instance
↓
Validate Identity
↓
Discover Capabilities
↓
Resolve Contracts
↓
Compose Publications
↓
Publish Organism
↓
Living Instance
Each conceptual stage is one or more of the concrete steps in Part 2:
| Lifecycle stage | Part 2 step(s) | What happens |
|---|---|---|
| Load Instance | LOAD |
The instance’s forge-instance.json is read from disk. |
| Validate Identity | VALIDATE |
Required fields, types, and unknown keys are checked; failures are loud and typed. |
| Discover Capabilities | DISCOVER |
Every forge-capability.json under the composition’s declared roots is found and validated. |
| Resolve Contracts | RESOLVE |
The instance’s enabled capability ids are matched against discovered registrations; domain capabilities are checked against declared domains. |
| Compose Publications | DERIVE |
Navigation and the runtime identity document are built as pure functions of the resolved composition. |
| Publish Organism | PUBLISH |
The identity document is written into the instance’s publication namespace, stamped with the moment of writing. |
| Living Instance | — | The published organism: one identity, one namespace, one navigation, reachable at its declared hosts. |
Nothing above is domain- or instance-specific. The same seven steps compose Mjolnir today and will compose the second organism tomorrow (Part 7) — which is the whole point of writing them down once, here, instead of once per deployment.
Part 4 — Identity
An instance identity is the validated form of forge-instance.json:
| Field | Meaning | Plane of the value |
|---|---|---|
id |
permanent instance identifier (kebab-case) | instance |
name |
display name | instance |
institution |
the operating institution | instance |
hosts |
one or more public addresses this organism answers to | instance |
publication_namespace |
namespace under public/ (defaults to id) |
instance |
timezone |
IANA timezone, validated | instance |
version |
optional organism version, informational | instance |
branding |
wordmark, logo, theme tokens, URLs | instance |
capabilities |
enabled capability ids — selection, not authorship | instance |
runtime_options |
named booleans for staged behaviour | instance |
domains |
per-domain settings, opaque to Core | domain vocabulary, instance values |
Two structural rules protect the planes:
- Domain vocabulary never becomes a Core field. A scheduler type or a software catalogue is HPC knowledge, so it lives inside
domains.hpc, a mapping Core stores and relays but never interprets. Core validates the shape (string settings, kebab-case domain ids); the meaning belongs to the domain that declared the parameter (FORGE-101 Part 5.3). - Configuration is a contract. Unknown keys, missing required fields, malformed types, and unknown timezones fail composition loudly. An instance cannot silently carry settings nothing declared.
hosts is a list, not a scalar, by design. The runtime assumes no single canonical address: during a migration an organism may answer at several hosts simultaneously — its permanent home, a legacy alias still in DNS, a path-scoped mount on another domain. Nothing downstream treats hosts[0] as more canonical than its siblings; a future need to distinguish a primary address is a field to add, not a redesign.
runtime_options replaces what earlier drafts called feature_flags. The rename is deliberate: “feature flag” invites an ever-growing, never-retired catalogue of permanent toggles. runtime_options names the same mechanism — booleans an instance declares to stage behaviour — while keeping the expectation that each entry is temporary scaffolding for a rollout, reviewed and removed once the behaviour it stages is simply how the organism works. public-organism (Part 7) is the first and, for now, only entry.
The runtime projects the identity into the runtime identity document — public/<namespace>/identity.json — the one artifact through which reader-facing surfaces learn who they are serving:
{
"runtime": {
"schema_version": "…",
"runtime_version": "…",
"instance_version": "…",
"status": "draft | published",
"generated_at": "…"
},
"instance": {
"id": "…",
"name": "…",
"organization": "…",
"hosts": [ "…" ],
"theme": { "accent": "…" },
"features": [ { "id": "…", "title": "…", "path": "/…/" } ],
"capabilities": [ … ],
"runtime_options": { … }
}
}
The runtime block describes the Forge Runtime and the publication act itself — engine and schema versions, the organism’s own declared version (when it declares one), whether this is a draft or a live publication, and when this document was generated — never scheduler or domain state; that distinction is what keeps this block Core’s to own. schema_version and runtime_version are engine constants (Part 2); instance_version, when present, is a value the instance manifest declares about itself. status and generated_at are the only fields not determined solely by the declaration — status follows the public-organism runtime option, and generated_at is stamped once, at the moment PUBLISH writes the file (Part 2).
The frontend consumes this document and nothing else. No page, stylesheet, or script in the engine carries an instance’s name, colours, institution, or URLs; static markup may carry a fallback rendering of the published document, never an independent statement. Branding flows in exactly one direction: declaration → runtime → document → surface.
Part 5 — Publication namespaces
Every published artifact belongs to exactly one instance namespace:
public/mjolnir/… Mjolnir's truth
public/rubus/… a future organism's truth
The namespace is the publication-time face of FORGE-100 Part 4.1’s instrument-scoped identifiers: two organisms can never collide, and federation later requires no redesign. PublicationNamespace places artifacts (public/<namespace>/<relative>), rejecting absolute paths and .. segments; an artifact cannot escape its organism.
Compatibility. Artifacts published before namespaces existed remain addressable. The LegacyPathBridge canonicalises an un-namespaced public/… path into the default instance’s namespace, and passes already-namespaced paths through unchanged. Old addresses keep resolving; new artifacts are born namespaced. Nothing breaks, and the bridge is the only place that needs to know the old form ever existed.
Part 6 — Capability registration
A capability is a major subsystem’s self-declaration to the runtime: Queue Insights, Software Intelligence, Scheduler, Storage, Telemetry, Reservations, Node Intelligence, Publication, and their successors.
Each capability ships a forge-capability.json beside its code:
{
"id": "queue-insights",
"title": "Queue Insights",
"plane": "domain",
"domain": "hpc",
"summary": "…",
"owner": "…",
"navigation": { "order": 10, "path": "/queues/" },
"version": "…"
}
The rules, each carrying its FORGE-101 obligation:
- Capabilities register themselves; the runtime discovers them. Discovery is declarative and closed at composition time (Part 7.3): the runtime scans the composition’s declared roots for manifests. There is no compiled-in registry of names, no
if capability == …anywhere. Adding a capability means adding a manifest, never editing the engine. Directories namedtestsare never composition sources. - Registrations are plane-owned. A core capability declares
"plane": "core"; a domain capability names its owning domain. An instance registers nothing — it enables, by id, from what the composed planes offer. - Resolution fails loudly. Enabling an id nothing registered, enabling a domain capability without declaring the domain, or registering the same id twice each fail composition with a typed error and a suggested fix.
- Navigation is derived, never authored. A capability may claim one navigation entry (order + instance-relative path); the runtime sorts claims deterministically and builds the navigation the identity document publishes. No hand-maintained menu exists to rot.
- Every registration names its owner (FORGE-102 ADR-032 lineage: nothing ships without a named upkeep owner).
- The manifest shape grows by addition, not by rewrite.
versionis the first field added to the registration schema since Capability 7A shipped, and it was added exactly the way the architecture intends: as a new optional key, validated the same waynavigationalready was, changing nothing about how existing manifests resolve. Richer capability metadata — declared state, deprecation, compatibility ranges — follows this same path when it becomes real work rather than speculation.
Part 7 — The organism
A composed instance is a living organism: one identity, one publication namespace, one set of enabled capabilities, one derived navigation — publishing its own truth on its own cadence, under its own name, at its own addresses.
The architecture already supports serving one organism cleanly at its declared hosts (Part 4): everything the organism’s surfaces need lives under public/<namespace>/, keyed off one identity document, with branding and navigation derived at composition time. DNS and deployment are deliberately out of scope; the public-organism runtime option stages the transition, and turning it on will be a recorded act, not a code change.
Because organisms are declarations, multiplying them is cheap by construction: a second instance is a second forge-instance.json plus a second namespace. Nothing in the engine changes, which is the entire point — and, per FORGE-101 Part 16.6, the second organism is the acceptance test of the first’s architecture.
Part 8 — What this document binds
- New engine subsystems intended for readers MUST register via
forge-capability.json; unregistered capability surfaces are invisible to every organism, by design. - New instance-varying values MUST enter through the instance manifest (a declared field or a domain setting), never through engine code.
- New published artifacts MUST be placed through the instance’s
PublicationNamespace; un-namespaced publication is legacy, tolerated read-only through the bridge. - The purity tests (no instance name in the engine; published identity documents match regeneration, timestamps excepted) are constitutional checks in FORGE-103’s sense: their failure is a build failure, not a warning.
- Adding a capability means adding a manifest, never editing the engine. No change to this document may weaken that sentence.