The CIM-56 model factory control plane.
DRAUPNIR is the ring that, in the Norse sources, drips eight further rings of equal weight every ninth night. The name is taken for a system whose function is to turn one shared substrate into fifty six derived models.
| Document | For |
|---|---|
docs/build/draupnir-sad.md |
The architecture (VLD-SAD-DRAUPNIR-001) |
docs/build/draupnir-ux.md |
The console and design system (VLD-UX-DRAUPNIR-001) |
| docs/CONTRIBUTING.md | Setting up a development machine |
| docs/DEPLOYMENT.md | Getting this onto the Sindri Forge |
| docs/runbook.md | A running system misbehaving |
| docs/PUBLISHING.md | Publishing and distribution naming |
Distributed as
veldris-draupnir. The import name isdraupnir; the prefix exists because an unrelated project ownsdraupniron PyPI.
make devOne command from a clean machine to a running stack with seeded data. On
Windows, .\make.ps1 dev. See docs/CONTRIBUTING.md.
| API | http://127.0.0.1:8000 |
| Console | http://127.0.0.1:5173 |
| API docs | http://127.0.0.1:8000/docs |
All twelve prompts of SAD section 13 are built: the repository, toolchain and
delivery pipeline; the core foundation -- the hash-chained ledger, the state
machine of SAD 6.1, the site registry and the run projection; the plug-in
system -- the seven interfaces of SAD 8.2, the entry point loader and a
conformance suite published for third parties; HODD and GLEIPNIR -- the
artefact store and the policy gate; MOTSOGNIR and HAMARR -- placement and array
concurrency, and the training executors; BRISINGAMEN, RAUN and SKIDBLADNIR --
reweighting, evaluation and release; SVALINN, GULLINBURSTI and MEGINGJORD --
the cross-cutting security layer and the federation; the HTTP surface of SAD
8.1, with the conventions of SAD 11E.2 wired to every route; JARNGREIPR, the
design system; the console, draupnirctl and the local view; the six
development skills of SAD 11G; and Procedures M1 to M10 end to end.
What is not built is the estate. SAD 1.3 puts the hardware in VLD-INF-SINDRI-001 and out of scope, so there is no three-appliance ring, no Slurm controller on REGIN, no NFS vault and no federation link. Every criterion whose evidence is a measurement on that hardware is marked DEVIATED or NOT BUILT in the reconciliation, with the reason, rather than marked IMPLEMENTED because it ought to work.
The ledger is the source of truth. run is a projection of it, rebuilt from
sequence 1 by a pure fold, and nothing else writes that table.
What the build enforces rather than asserts:
- Every stage of the pipeline in SAD 11H exists and runs, and none is skippable on main.
- The inward-only dependency rule of SAD 11B is enforced by an import linter from the first module, rather than asserted and checked at review.
- The three database-enforced constraints of SAD 11C are in the first migration and are attacked by integration tests.
- The generated CLI and TypeScript client are regenerated by the build, which fails on drift.
- The state machine is data: fifteen transitions, each with a named guard and the ledger fields SAD 6.1 requires it to record. Adding a state means adding rows, and every transition has a test the build refuses to let you forget.
- A site scope is chosen, never inferred. A repository cannot be constructed without one, and the queries carry an explicit predicate as well as relying on row level security.
- A deterministic seed writes chains, not rows: 2 sites, 6 sources, 13 runs resting in every run state, 1 release and 400 hash-chained ledger entries.
- A driver is an installation, not a core change. Nothing in DRAUPNIR names a driver, and an import contract forbids a driver from reaching into the core.
renderis pure, and the conformance suite proves it rather than asking: three renders agree, no socket opens, and the working directory is untouched.- HODD records licence facts and cannot judge them: an SPDX identifier
anywhere in
hodd/outside a docstring fails the build. - Ingest is atomic. Each of its four failure points is tested by crashing there, and none leaves a half-registered corpus.
- The single-approver exception of constraint C-11 is computed, never supplied, and sits inside the signed payload -- so suppressing it invalidates the signature and the release is refused.
- A ring run refuses to plan when an appliance is down rather than running on two nodes of a three node specification. An adapter array reduces its concurrency instead, because losing a machine there costs throughput and nothing else.
- Fifty six elements go in as one submission,
--array=0-55%3, where the%3is a throttle rather than a count -- so utilisation does not depend on the control plane being awake. A failed element is retried as--array=<index>, which is what keeps the other fifty five untouched. - The checkpoint interval is derived from observed step time, not authored: the largest interval that keeps unwritten work under thirty minutes, provisional at submission and recomputed once fifty steps have actually run.
- Progress reaches the UI as numbers. The regular expressions live in the driver, so a change to LLaMA-Factory's log format is a plug-in version bump and touches no core file.
- The tier table drives base selection and validates at submission that the two lists enumerate all fifty six with no duplicate and no omission (AC-F16). An unknown jurisdiction raises; there is no default tier.
- An unknown base model raises rather than resolving a default chat template. The alternative failure is silent: training proceeds, the loss curve looks ordinary, and the damage surfaces at evaluation days later.
- Gate results bind to the artefact's SHA-256 and to nothing else.
Evidencecarries no path, no URI and no bucket, and a test walks its fields and fails if one is added -- because the way this control decays is somebody adding a URI for a console's convenience. Publish re-hashes the bytes it is about to release and refuses on mismatch. - Every built format is re-gated, and the check is driven by what was built rather than by what was evaluated. Iterating the evidence would confirm that everything evaluated passed, which is true of an empty set.
- The weight sweep is one object, not five runs sharing a naming convention. The comparison is a method on it and the selected point is recorded on it, so what the console showed and what the model card publishes are the same fact.
- The model card is rendered from
Facts, each either recorded with a source or absent with a reason. There is no path by which a missing value becomes a missing row. - Article 53 artefacts are generated from the licence register, on a versioned AI Office template recorded with the release. Article 50 belongs to the Midgard Suite: a test reads this package's own AST and fails if anything here defines or imports watermarking.
- The MLX build is compared against the NVFP4 build and raises beyond a threshold tighter than any gate margin. A quantisation defect looks like a healthy model; what catches it is two builds of the same weights disagreeing.
- A route without an explicit role declaration prevents the application from starting. Not refused at runtime -- the process does not come up, so it is a CI failure rather than a log line somebody reads after the first call.
- Secrets are leases. The value has one way out, named
reveal; the repr and str are redacted; and the job environment carries lease references, so there is no point at which the control plane could write a secret into a job environment file. - Every outbound call declares a destination, a purpose, a run and an approving policy, and an undeclared destination fails. The teacher-model destination is absent, and a test fails if anybody adds it.
- Executors run rootless with no network namespace and read-only artefact
mounts, and every weakening is an absence rather than a defaulted field: the
profile has no
allow_networkto set. - The signature envelope carries a list of signatures from version one, and verification succeeds against any accepted algorithm -- which is what lets a post-quantum migration be incremental rather than a re-signing of history.
- The cryptographic inventory is a build artefact generated from the constants the system uses. Adding an algorithm to the envelope and forgetting the inventory fails the build.
- MEGINGJORD cannot hold a corpus or a weight. Every federation payload is built through a seal that walks the structure and refuses anything that is not a hash, a name, a timestamp or a number, and there is no path that serialises an unchecked one.
- Every mutating endpoint requires an
Idempotency-Key, and a key is reserved before the work starts -- so a second click while the first request is still running is refused rather than acting twice. - Pagination is cursor based everywhere. A cursor is a position in the
(created_at, id)order rather than a count of rows skipped, so a row inserted mid-pagination changes what comes next and never removes something that was going to come. - Conditional writes are required, not optional: a mutation with no
If-Matchis 428 and a stale one is 412. Optional concurrency control is control the one client that needed it did not use. - Server-sent events carry deltas. An event listing no changed fields is refused, because an event that carries no delta is a refresh instruction.
- Every log line carries run id, site id and actor, and the redaction is in the emitter rather than in the caller -- so a rule that only applies to careful callers is not the rule.
- The OpenAPI document is the single source for both clients. The drift gate regenerates and fails the build if either was edited by hand.
- JARNGREIPR, the design system, ships 19 primitives and 11 composites, and every one of them ships six states beyond the happy path: loading, empty, error, denied, read only and partitioned. That is one shared contract rather than a convention, because a convention followed component by component is a convention the twentieth component skips. 220 Storybook stories, 30 components at seven states each, and a test that fails the build if a component ships fewer. Those figures are checked against the stories themselves, so a component added without them fails the build too.
- Tokens are the only source of visual values, enforced by a linter rather than a guideline: a hard-coded colour, spacing or radius fails the build. The linter's own fixtures prove it fails, because a gate nobody has watched fail is a gate nobody knows works.
- Both colour ramps are measured rather than asserted. The contrast test recomputes every declared pairing from the stylesheet and found the control border at 1.5:1 against a 3:1 requirement; the ramp changed, not the threshold.
- axe runs over every route and over all 220 stories, so the denied and partitioned states -- the ones nobody looks at while building -- are checked as hard as the happy path.
- The console and
draupnirctlare both generated clients of one API. There is no method per operation in either: the operation table is generated fromopenapi.jsonand one generic caller performs the request, so a hand-written client method is not something a reviewer has to catch -- there is nowhere to write one. - A run's identity and a run's identifier are different things. The identifier is a UUIDv7 and says when the run was created; the identity is the hash of the specification and its resolved input artefact hashes and says what the run is. Submitting the same file twice is two runs with one identity, which is exactly the relationship an operator wants when comparing them.
- The dry run is the primary action on the compose screen and submission is the secondary one. An allocation on this estate is the scarce resource, so a specification error should cost nothing to find, and submitting without a dry run takes an extra confirmation.
- The gate queue puts the evidence and the sole approver notice above the decision controls, and the controls stay unavailable until the evidence has actually been on screen. Evidence an approver can scroll past without noticing is evidence they can decline to read.
- CON-A, the local status view on DVALIN, imports no HTTP client and nothing
from
draupnir. It is tested with every source unavailable, because the case where everything else is broken is the case it was bought for. - All thirty one screens of the inventory are built. Two of them publish a
table that is enforced somewhere else rather than a copy of one: the policy
screen renders the
Policyobject the licence gate decides with, and the role screen renders the same@needsdeclarations the guard reads at request time. Neither can drift from what actually happens, because neither is written twice. - The ledger entry viewer recomputes the entry hash from the previous hash and the canonical payload and shows both. A viewer that rendered the stored hash would prove nothing: the stored hash is exactly what a tamperer would have rewritten.
- An attestation over an incomplete chain exports unsigned and says so. Signing it would certify the gap.
- CON-B, the wall panel, states its own staleness in words and rotates its three dashboards on a timer. A panel whose numbers stopped an hour ago is worse than a blank one: nobody is watching closely enough to notice, and the numbers are believed.
Verified: 100,000 ledger entries verify in about four seconds against the sixty second budget of AC-N5, and a rebuild of the run projection is byte-identical to the one before it. AC-N9 is demonstrated end to end: a new export format, discovered by entry point, rendered, run through the reference scheduler and collected, in 75 lines against a 200 line budget.
Sindri is a forge — a site, a set of machines that together run one estate — rather than a host. What deploys at Sindri is the control plane, and SAD Decision S3 puts that on ALVISS rather than on an appliance, because the appliances are the scarce resource and a control plane on one is a control plane that an out-of-memory kill takes with it.
| Host | Runs |
|---|---|
| ALVISS | draupnir-api, draupnir-worker, draupnir-web, as rootless containers |
| ANDVARI | PostgreSQL 16, MinIO, the HODD vault, the image registry |
| REGIN | Slurm controller, Loki |
| DVALIN, DURIN, DAIN | The three appliances. Executor shims, one container per job |
| Veldris_NXT | MEGINGJORD, the federation registry |
The control plane holds no state. Everything is in PostgreSQL and MinIO on ANDVARI, which is why it can be destroyed and rebuilt without losing a run, and why a restart interrupts the console and not the training.
./deploy/install.sh --check # preflight; changes nothing
./deploy/install.sh --revision <sha> --site sindri # commission a new ALVISSinstall.sh creates the three units; rollout.sh and rollback.sh move them
between revisions and are what the pipeline's stage 4 runs. Full procedure,
secrets, migrations and rollback in docs/DEPLOYMENT.md.
The installer does not install PostgreSQL or MinIO. SAD 7.2 puts both on ANDVARI as existing instances, so it checks that they answer and stops if they do not: an installer that stood up its own database would give the site a second one, and the ledger would be in whichever the API happened to reach.
| Path | Contents |
|---|---|
draupnir/ |
The application. One package per module of the table below |
web/ |
JARNGREIPR and the console. A pnpm workspace |
deploy/ |
Commissioning, rollout and rollback for ALVISS |
docker/ |
The two images the pipeline builds, and the development stack |
docs/acceptance/ |
One file per criterion, generated from the specification and the citations in the tree |
skills/ |
The six development skills of SAD 11G |
scripts/ |
Operator and build tools that tasks.py wraps |
tests/ |
unit, property, contract, integration — the pipeline's stages 2.1 to 2.4 |
tasks.py |
Every task the pipeline runs. python tasks.py --list |
tasks.py is the single entry point: the pipeline runs the same commands a
developer does, so a stage that passes locally and fails in CI is a bug in the
task rather than a difference between two scripts.
| Module | Responsibility |
|---|---|
| Core | Workflow state machine, run registry, audit ledger, event bus, plug-in loader, API |
| HODD | Corpus and artefact store. Immutable ingest, hashing, licence register, retention |
| GLEIPNIR | Policy and assurance gates. Licence, data protection, approval, release sign off |
| MOTSOGNIR | Job dispatch and placement |
| HAMARR | Training executors |
| BRISINGAMEN | Reweighting and merge |
| RAUN | Evaluation and assurance |
| SKIDBLADNIR | Quantisation, packaging and release |
| SVALINN | Cross cutting security |
| GULLINBURSTI | Per forge site agent |
| MEGINGJORD | Federation registry |
Veldris. Internal. Not for distribution.