**What starts a job.** An `on` block, at the top of the pipeline or in a job (the job's wins), names the events that run it: `push` with bookmark globs (none: any bookmark), `tag` with tag globs (none: any tag), `manual` ("Run pipeline", `oh run start`) and `schedule` with five-field cron lines on the hub's clock (UTC), run on the default bookmark. Without `on`, a job runs on every push and by hand, as before. A run has only the jobs its event starts (and what they depend on); an event that starts no job starts no run. A rerun repeats its run's event.
**Time limits.** A job may say how long it may run with `timeout "90m"` (hours, minutes and seconds, as `"2h"`, `"1h30m"` or `"45s"`, from a minute to a day); a sandbox's `timeout` is the default for its jobs. Without one, the hub's default applies, and the hub caps every job at the site's maximum (both set by an administrator on Admin → Runners). The runner stops a job at its limit, and the hub ends one its runner stopped reporting a few minutes after it. A timeout is part of the compiled job, so a job without one compiles as before.
**Runner labels.** A sandbox or a job may say `runs-on "LABEL" ...`: lower-case letters, digits, `-`, `_` and `.`, at most 64 bytes each, given on one or more lines. A job needs a runner that has every label its sandbox and it name. A runner reports its labels and architecture when it registers and on each claim (`flow runner register --label`). The hub hands a job only to a runner with all of them, within the runner's scope (OxygenHub spec 13.2), and a job no runner can take waits, saying so on the queue. Labels are part of the compiled job, so a job that asks for none compiles as before.
"hermetic": bool, // Verified zero outbound network access
348
"reproducible": bool
349
}
350
}
351
}
352
```
353
354
###7.2 Composite Signatures
355
All provenance attestations are signed using Oxygen's composite signature
356
construction (Ed25519 and ML-DSA-65):
357
358
```cbor
359
Signature {
360
key [0]: KeyId
361
alg [1]: 1 // 1 = Ed25519 + ML-DSA-65
362
ed [2]: bytes(64) // Classical signature
363
ml [3]: bytes(3309) // Post-quantum signature
364
}
365
```
366
367
Both signatures are computed over:
368
"oxy1 sig attest\0" || KeyId || ObjectRef
369
370
##8. Policy Integration & Bookmark Gates
371
372
Oxygen repositories enforce branch and bookmark protections via signed Policy
373
objects (Oxygen Spec Section 14.5). OxygenFlow integrates provenance directly into
374
policy evaluation during push validation.
375
376
###8.1 Policy Gate Definition
377
A Policy rule governing a bookmark MAY demand valid OxygenFlow attestations. To mitigate a compromised authorized worker, high-security repositories can require **Consensus Builds** by setting the threshold > 1. This requires multiple independent runner identities to arrive at the exact same output hashes and both sign it.
| `tiers` | The sandbox tiers it can honour: `microvm` only with `/dev/kvm` usable (a board without KVM, such as an OrangePi RV2, offers `syscall`, §4.2, and `wasm`) |
469
| `memory_mib`, `cpus`, `max_jobs` | What its jobs share |
470
| `images` | The rootfs images it holds, each with the architecture its manifest records (§11.2) |
471
| `labels` | As §3.1 |
472
| `speed` | A class from 1 (slow) to 5 (fast), measured at `flow runner register` and `flow runner bench` by a fixed workload (BLAKE3 over 256 MiB and the compilation of a fixed C file), bucketed so that two machines of the same class finish a job within a factor of two of each other |
473
| `load` | The 1-minute load average divided by `cpus`, memory free now, jobs running |
474
475
### 11.2 Image Manifests Carry the Architecture
476
477
`flow rootfs import` reads the ELF header of the image's `/bin/sh` and
478
records its architecture in the CAS manifest beside the hash. A runner
479
refuses to import an image for another architecture (there is no
480
emulation tier). An image imported before this section has no recorded
481
architecture and is treated as the importing runner's.
482
483
### 11.3 Job Requirements
484
485
Hard requirements, all of which a runner MUST satisfy to be offered the
486
job: the sandbox's tier, its rootfs (hence the image's architecture),
487
`memory-limit`, `cpus`, every `runs-on` label of the job and its sandbox,
488
and the runner's scope (OxygenHub spec §13.2). Two additions to the
489
language:
490
491
- `arch "riscv64"` on a sandbox pins the architecture; without it the
492
architecture is the rootfs image's.
493
- A rootfs per architecture: `rootfs { x86_64 "cas://blob/…"; riscv64 "cas://blob/…" }`.
494
The job runs on whichever architecture takes it; the receipt and the
495
provenance record the architecture and image used, and a check's
496
description names them.
497
498
Soft requirements guide placement but never block it: a job's `timeout`
499
against the runner's speed class. The hub keeps, per job hash and speed
500
class, the median duration of past runs; a runner whose class predicts a
501
duration over the limit is offered the job only when no better runner
502
is free.
503
504
### 11.4 Placement
505
506
The hub filters by the hard requirements, then ranks the runners that
507
remain: idle before busy (`load`), a higher speed class first, a runner
508
already holding the image before one that must fetch it (§5), fewer jobs
509
waiting on it first. A job no runner can satisfy waits and the queue
510
says what it waits for: tier, architecture, image, memory, labels or
511
scope, so the gap is visible rather than a silent stall.
512
513
### 11.5 Declining
514
515
A runner double-checks a claim before it starts: the architecture, that
516
`/dev/kvm` opens for the MicroVM tier, that the image's blob is present
517
and verifies, that the memory is free. If any check fails it answers the
518
hub with `declined` and a reason instead of running and failing; the hub
519
re-queues the job, never offers it to that runner again until its
520
profile changes, records the decline on the runner's page, and after
521
three declines by different runners marks the job `unplaceable` with the
522
reasons. A decline is never a failed check on the state.
523
524
### 11.6 Conformance
525
526
1. Architecture Safety: a job whose image is `x86_64` MUST never be
527
offered to a `riscv64` runner, with or without the image copied there.
528
2. Tier Safety: a runner without `/dev/kvm` MUST NOT be offered a
529
MicroVM job, and MUST decline one if handed it.
530
3. Room: a job whose `memory-limit` exceeds the runner's free memory
531
MUST wait, not be killed.
532
4. Visibility: a job nobody can take MUST say why on the queue within
533
one scheduling pass.
534
5. Preference: with two idle runners that both satisfy a job, the hub
535
MUST offer it to the higher speed class, and to the one holding the