Skip to content
Agents' Last Exam for Robotics
Scoring & trust

Submission bundle

The <run-id>.ale-submission.tar.zst.age format, its allowlisted files, how to obtain the encryption key, and the exact server-side validation order.

A submission bundle is the single, encrypted artifact a local runner produces after a run. It carries everything the website needs to independently re-check a result — and nothing it should not. This page specifies the wire format, the allowlisted file set, and the exact order in which the website processes an upload.

Format

The file name is <run-id>.ale-submission.tar.zst.age. The extensions read outside-in and describe how the runner builds it, inside-out:

age( zstd( tar( allowlisted files ) ) )
  1. tar — the allowlisted files (below) are packed into an uncompressed tar archive.
  2. zstd — the tar is compressed with Zstandard.
  3. age — the compressed archive is encrypted with age (X25519) to the website's

public key, using the audited pyrage library.

Encryption is never hand-rolled. The runner holds only the website's public key; the matching private key lives on the website and is never committed to a repo, baked into an image, written to logs, or sent to a browser. See the Security model.

Allowlisted files

The bundle contains exactly these files. Anything else in the archive is ignored on extraction. Each JSON file is validated against a schema in packages/contracts/schemas.

FileContents
manifest.jsonRun identity and binding: submission_id, challenge_token, nonce, benchmark_version, required_task_set_hash, agent, model, container_images (name + digest), resource_limits, started_at/finished_at, and the integrity_root.
events.jsonl.zstThe zstd-compressed, hash-chained trajectory event stream (schema ale-trajectory-event/v1). Validated via its hash chain, not as JSON. See Trajectory format.
task_results.jsonPer-task evaluator results: task_slug, measured_metric, claimed_normalized_score, num_trials, stderr, and an evaluator_result_hash.
score_breakdown.jsonThe claimed aggregate: claimed_aggregate_score, aggregation_policy_type, and a per_task breakdown. See Scoring.
faithfulness.jsonReproduction-genre judge report: applicable, passed, and per-check detail. See the Faithfulness gate.
network_summary.jsonOffline-sandbox attestation: sandbox_offline plus any recorded anomalies.
artifacts_manifest.jsonA list of run artifacts by kind, path, sha256, and size_bytes (a manifest, not the artifact bytes themselves).
integrity.jsonIntegrity anchors: integrity_root, event_chain_head, event_count, and the hashing algorithm.

All JSON files carry schema_version: "ale-robotics-submission/v1" (the trajectory stream carries ale-trajectory-event/v1).

What is excluded

Bundles never contain hidden seeds (stored only as a commitment; see Trajectory format), hidden reference solutions, API keys, private prompts, hidden model reasoning / chain-of-thought, or unredacted full trajectories. The domain metric is always the evaluator's, never self-reported.

Obtaining the public key

Fetch the current key from the API. This is public and requires no authentication:

GET /api/v1/submission-key
{
  "algorithm": "age-x25519",
  "public_key": "age1...",
  "key_id": "...",
  "schema_versions": ["ale-robotics-submission/v1"]
}

The runner encrypts to public_key. Before uploading you must also obtain a one-time challenge via POST /api/v1/challenges (auth required) and record its challenge_token and nonce in manifest.json. Upload the ciphertext to POST /api/v1/submissions.

Server-side processing order

The website is not a benchmark runner — users run locally, then upload. On receiving a bundle the server runs a fixed, fail-closed pipeline. Each step must pass before the next:

  1. Receive ciphertext, compute its SHA-256.
  2. Decrypt with the private key.
  3. Enforce limits on decompressed size and file count (zstd bomb-safe cap).
  4. Safe tar inspect — reject path traversal, symlinks, hardlinks, devices, FIFOs, and

absolute paths.

  1. Extract allowlisted files only.
  2. Validate JSON schemas for every present JSON file.
  3. Verify the challenge token (server-signed, one-time, unexpired) and its nonce,

bound to this submission and user.

  1. Verify hash chains over events.jsonl.zst against integrity.json.
  2. Verify the integrity rootmanifest.integrity_root must match integrity.json.
  3. Verify benchmark/task versions + coverage against the release manifest.
  4. Recompute scores from the evaluator results and compare to the claimed values.
  5. Audit summary, then persist metadata.
  6. Delete the plaintext and ciphertext temp files.

Successful decryption does not grant a verified status. The recomputation and gate results determine the verification level; the backend refuses any attempt to self-elevate via an upload field. Bundle retention defaults to KEEP_SUBMISSION_BUNDLES=false, so plaintext and ciphertext are removed after processing.