Running one job (heimdall-run)
Heimdall is a defensive sandbox. heimdall-run takes one wasm job end to
end. The caller trusts only the signed result it produces, never the untrusted
input or the guest's bytes.
Steps
- Load the policy file (see POLICY.md).
- Create the out dir (rules below). Nothing has run yet; if this fails, the job does not start and no result is written.
- Read the input dir (rules below).
- Run the guest through heimdall-wasm with the policy's limits. The guest's
emit-file(name, bytes)calls become files inout/files/. - Hash every output (sha256) and audit
out/files/: it must hold exactly the files Heimdall wrote, unchanged. - Build the JobResult (verdict, outputs with hashes, start and finish time) and
sign it with heimdall-core's per-boot key. Write it to
out/result.signed.
Input and out-dir problems stop the job before the guest runs; then no result exists (and an input problem leaves no out dir behind). Everything after the guest started ends in a signed result:
| What happened | Verdict | Outputs kept |
|---|---|---|
the guest returned ok and every file was written | Accepted | all |
the guest returned err(refusal) | Refused (the guest's reason) | the files it emitted (a refusal report is a legitimate output) |
a trap or a limit hit (guest trapped (<why>)) | Refused | none |
| an emitted name broke the rules, a name came twice, or an output cap was hit | Refused (output refused (<why>)) | none |
out/files/ did not match what Heimdall wrote | Refused (output audit failed (<why>)) | none |
The result lists the outputs in emit order with their sha256 and length, a
fuel event (used, limit), one emit-file event per file (a refused one is
logged as Refused), and the run's isolation features.
The key
One heimdall process is one boot: it makes a fresh Ed25519 key
(heimdall-core BootKey), never writes the secret half anywhere, and hands the
caller the public record (heimdall-boot-key v1 <key id> <boot id> <public key>)
on stdout and, with --pin-out <file>, in a new file. The caller pins that
record and verifies the result with it.
The input dir
In milestone 1 a wasm job takes exactly one input.
- The input dir must exist and be a real directory (a symlink is refused).
- It must hold exactly one entry, and that entry must be a regular file. A symlink, a subdirectory, a FIFO or a device is refused.
- The file is opened with
O_NOFOLLOW|O_NONBLOCKand re-checked on the open fd. - It may be at most
max_input_byteslong. Its bytes become the guest'sinput. - Heimdall never writes to the input dir.
The out dir
- The out dir must not exist, not even as a dangling symlink. Heimdall
creates it (mode 0700) with a plain
mkdir, so nothing is ever overwritten and a racing symlink is never followed. Its parent must already exist. - Layout:
out/files/<name>holds the job's files,out/result.signedholds the signed result. A guest file can therefore never collide with the result. - An output name is one flat file name: 1 to 255 bytes of
[A-Za-z0-9._+-](the same rule heimdall-wasm applies toemit-file), not starting with.. So: no/, no.., no hidden files, no spaces or control characters, no non-ASCII look-alikes. - Every file is created with
O_CREAT|O_EXCL|O_NOFOLLOW, mode 0600. A name emitted twice, or a symlink planted in the way, is refused. - The number of files, each file's size and the total size are capped by the policy.