GPUDECK · SHADOW MODE · THE BOUNDARY

What shadow mode would need from your fleet

GPUDeck runs only on our reference fleet today and no outside operator has installed it. This page is the boundary an integration would have to respect — written before there is an installer, so the offer can be judged on what it actually asks for.

Shadow mode means it cannot act

Shadow mode observes and records. It takes no remediation, so the first integration on an outside fleet cannot restart a container, change a config or touch a running job. That is the point: the first thing to establish is whether the measurement works on someone else’s estate, and nothing about remediation needs to be trusted to answer that.

9 of 9 questions are answered below.

Who answered what, and why it matters

Several of these are about what runs on the fleet, and this record deliberately cannot see it. GPUDeck receives files; it does not read the fleet's machines, its scripts or its logs, and the repository that builds this site has a standing rule against doing so. That separation is why the incident schema can promise what it promises — a record that could reach into the fleet to enrich a row is a record that could leak one.

So the answers below are labelled by who established them. Where the entry says fleet session, GPUDeck is repeating an operator's account of their own machines and could not independently confirm it; where it says gpudeck, the claim is checkable from the receiving end. Those are different strengths of evidence and collapsing them would be the same substitution error this project exists to refuse.

The boundary, question by question

What data leaves the operator's machine?

answered

Seven core fields per incident, plus optional enrichment, plus one provenance field. Nothing else - and that is now enforced as an ALLOWLIST rather than a list of banned names: a key outside the schema is refused, because a denylist can only refuse the identifiers somebody anticipated. Core: observed_utc, incident_class, demand_evidence, action, action_mode, detection_latency_s, time_to_earning_state_s. Optional: incident_id, symptom, in_flight_job_at_action, recovery_evidence, outcome, claim_boundary, remediation_source. Provenance: definition_hash, which names the edition of the rules the row was produced under.

Evidence: gpudeck_incident_ledger.DEFINITION(), hashed into the edition identity. The definition_hash field was omitted from an earlier version of this answer while appearing in the sample row below - a page claiming an exhaustive list that its own example contradicted.
Verified by: gpudeck

What is refused, always?

answered

Thirteen field names are refused outright. They cover payment identity (the crypto address, the payout amount, the earnings figure), customer and job identifiers, worker, lane and node identity, the prompt text, and the fleet's topology. A row carrying any of them as a KEY is rejected rather than stripped - the row is refused and the reason recorded, so a leak becomes a visible rejection instead of a silent edit.

Evidence: FORBIDDEN_FIELDS in gpudeck_incident_ledger.py, hashed into the definition identity so the list cannot change without the edition changing. The literal field names are deliberately NOT reproduced on this page: the public build runs a redaction scanner over every staged byte, it cannot tell a document naming a banned field from one leaking one, and it refused this page when the list was inline. Weakening that scanner to publish a document about not leaking would be the wrong trade, so the names live in the code and the spec.
Verified by: gpudeck

Are values checked, or only field names?

answered — reported, not verified here

The fleet reports that its emission guard refuses on VALUES as well as keys - a symptom string naming a host, carrying a lane label, or containing a job GUID is refused, because an interpolated log line is easier to forget than a key list. They report proving the guard fires by injecting such rows rather than observing that it did not, and an independent audit of all emitted rows finding zero violations.

Evidence: Reported by the fleet operator 2026-08-28. GPUDeck can confirm the rows it RECEIVED carry no identifiers - see the sample row published on this page - but cannot verify the producer's guard from the outside. Stated as their account.
Verified by: fleet (reported), gpudeck (received rows only)

How does it leave, and what else travels with it?

answered — reported, not verified here

For the reference fleet: JSONL files, one per observation date, copied by a fleet-side scheduled task into a directory GPUDeck reads. There is no agent, no callback, no network listener and no port open on GPUDeck's side to receive anything - the rows are already-written files that appear in a folder. Nothing else travels with them: no telemetry, no error reporting, no heartbeat, because there is no channel for those to travel on. For an OUTSIDE fleet this is not yet designed, and the reference fleet's arrangement should not be read as the answer. A file drop works when both ends are one operator's machines; it is not obviously what a stranger would want, and choosing it is part of the first assisted integration rather than a decision already taken.

Evidence: Verifiable from GPUDeck's side without trusting the producer: the incident files are ordinary files with mtimes in the handoff directory, and the repository contains no listener, ingest endpoint or inbound network path - the only HTTP server in the tree serves localhost for a performance baseline. What GPUDeck cannot see from here is what runs on the fleet host to produce and copy them; that is the open question above it.
Verified by: gpudeck (receiving end), fleet (sending end, still open)

What does shadow mode read?

answered — reported, not verified here

Five local files, polled hourly, all plain files on the same host - no sockets, no APIs, no continuous tailing. throughput_archive/worker-kicks.jsonl (kick time, network reason, detection time); fleetdeck_worker_kick_selfheal.log (what remediation did); fleetdeck_interlock_lifecycle_driver.log (per-lane job completions, used for demand evidence AND recovery); fleetdeck_cgroup_oom_watch.log (OOM kills); fleetdeck_image_rollout_loop.log (config-drift holds). Nothing is read that is not used: drop OOM_RISK and CONFIG_DRIFT and the last two files become unnecessary.

Evidence: Scheduled task trigger is PT1H. All reads are [System.IO.File]::ReadLines on the five paths above.
Verified by: fleet session, 2026-08-28

What permissions does it require?

answered — reported, not verified here

None elevated. No root, no Docker socket, no group membership, no access to another user's files, and no network egress at all - the producer makes zero outbound calls. It needs read access to four log files and one JSONL in the FleetDeck working directory, and write access to that directory's throughput_archive/. It runs as an ordinary scheduled task under the operator's own account.

Evidence: Zero matches for any network or process-control cmdlet. Scheduled as FleetDeckIncidentLedger with LogonType S4U, RunLevel Limited - explicitly NOT elevated.
Verified by: fleet session, 2026-08-28

What does it write or change on the host?

answered — reported, not verified here

Nothing but its own output. It creates a temp file and renames it over throughput_archive/incidents-YYYY-MM-DD.jsonl. It restarts nothing, rotates nothing, changes no config. Observation and remediation do NOT share a code path here - not by design, but because they were written as different scripts at different times and never joined. That is a weaker guarantee than a designed separation and should be read as such: nothing enforces it, and a future edit could couple them without any check failing.

Evidence: Only two write calls exist in the file (lines 522-523), both onto its own output path. Remediation lives in Invoke-FleetDeckWorkerKickSelfHeal.ps1 and Invoke-CloreRentedLaneLifecycle.ps1, which the producer neither imports nor invokes.
Verified by: fleet session, 2026-08-28

Does a shadow mode exist today, or would it have to be built?

answered — reported, not verified here

YES for the incident producer, and only because it has no remediation to switch off. Update-FleetDeckIncidentLedger.ps1 reads four log files plus one JSONL and writes one JSONL. It has no flag because it needs none. But do not read that as 'GPUDeck has a shadow mode': the events it records are PRODUCED by watchers that do act - the kick self-heal restarts containers - and those live in separate scripts. A GPUDeck install that shipped only the producer would be observe-only today; one that shipped the watchers that generate its input would not.

Evidence: Static read of Update-FleetDeckIncidentLedger.ps1: two write operations in the whole file, both to its own output (WriteAllLines to a .tmp, Move-Item onto incidents-YYYY-MM-DD.jsonl). Zero matches for Invoke-RestMethod, Invoke-WebRequest, Start-Process, Stop-Process, ssh, docker, Restart-*.
Verified by: fleet session, 2026-08-28, by grep over the producer source

What is specific to our fleet and would break on yours?

answered — reported, not verified here

Most of it. Be blunt with a stranger: this producer would emit nothing on their estate without being rewritten. (1) THE BIGGEST ONE - demand_evidence, the rule the whole ledger rests on, is derived from fleetdeck_interlock_lifecycle_driver.log, a FleetDeck-specific script no outside operator has. Without an equivalent completion stream every row is demand_evidence=NONE and nothing qualifies. (2) It parses exact log prose we wrote: 'worker nft=<n> ... start #', 'STEP job-end ok', 'memory-cgroup OOM kill', 'status=held', 'code 4015'. Any of these changing breaks it silently. (3) It assumes the Sogni NFT-per-worker model - incident identity is a lane, and a fleet without NFTs has no equivalent key. (4) Windows plus PowerShell 7 plus Windows scheduled tasks. (5) Hard-coded file names in one working directory, not a config. (6) It assumes remediation writes a log it can read afterwards; an orchestrator that remediates without leaving that trail yields AUTOMATIC rows we cannot attribute.

Evidence: Read of the parse patterns and source paths in the producer.
Verified by: fleet session, 2026-08-28

One real row, reformatted but not edited

Describing a schema is cheaper than showing what it produces. This is an actual incident row from the reference fleet. Every key and value is exactly as emitted; only the formatting differs, because it is stored as one line of JSON and is printed here indented with its keys sorted. Nothing was removed or rewritten — and saying “unedited” of something reserialized would have been the kind of small overstatement this whole record exists to refuse:

{ "action": "the container was restarted under its own restart policy; no operator action was taken", "action_mode": "AUTOMATIC", "claim_boundary": "Does not claim the kill was preventable, nor that a remediation avoided one. Paid work in flight at the moment of the kill was destroyed by the kill itself, not by any action recorded here. Time to earning state is measured from the kill, not from a remediation, because none was taken.", "definition_hash": "a03c21b1ae0c34f3", "demand_evidence": "PEER_WORKERS_COMPLETING", "detection_latency_s": 415, "in_flight_job_at_action": false, "incident_class": "OOM_RISK", "incident_id": "INC-2026-08-28-001", "observed_utc": "2026-08-28T00:07:05.6911159+00:00", "outcome": "RECOVERED", "recovery_evidence": "a credited completion after the kill, taken from the independent completion stream", "remediation_source": "RUNTIME", "symptom": "the render process was killed by the memory cgroup while resident near its cap, destroying whatever it was rendering", "time_to_earning_state_s": 126 }

Note what is absent: no host, no lane or worker identifier, no job id, no earnings, no prompt. The prose fields are descriptions rather than log excerpts, because an interpolated log line is the easiest place for an identifier to survive.

What we would ask of a first operator

Not to install something. To do the first integration with us, on a call, with every manual step written down as we hit it. What that produces is the install contract — the list of things this system assumes about a fleet that turn out to be assumptions about ours. That list does not exist yet, and it cannot be written from the inside.

The founding cohort is where that starts.