Arpan Tripathi
add metagraph resync & alpha stake finalisation in competitions' enrolling phase (#189)
4758a24 unverified |
Download docs/validator_setup.md from silvanus0930/vidaio-subnet: direct link, hf CLI and curl.
- Browser
- Download file 30.7 kB
-
https://huggingface.co/silvanus0930/vidaio-subnet/resolve/main/docs/validator_setup.md
- Command line
-
hf download hf://silvanus0930/vidaio-subnet/docs/validator_setup.md
-
curl -L -o validator_setup.md https://huggingface.co/silvanus0930/vidaio-subnet/resolve/main/docs/validator_setup.md
30.7 kB
| # Running Validator | |
| --- | |
| ## Machine Requirements | |
| To achieve optimal results, we recommend the following setup: | |
| - **Operating System**: Ubuntu 24.04 LTS or higher | |
| [Learn more about Ubuntu 24.04 LTS](https://ubuntu.com/blog/tag/ubuntu-24-04-lts) | |
| - **GPU**: NVIDIA RTX A6000 with 48GB VRAM and 30 CPU Cores | |
| --- | |
| ## Bootstrap System Dependencies | |
| The `bootstrap.sh` script at the repository root automates the installation of core system-level dependencies: | |
| - **NVIDIA GPU drivers** (default version 535) | |
| - **Docker** and the **NVIDIA Container Toolkit** | |
| - **Python 3.11** | |
| - Base utilities (git, curl, wget, etc.) | |
| Run the script **as root** with the `-E` flag to preserve environment variables: | |
| ```bash | |
| sudo -E ./bootstrap.sh | |
| ``` | |
| > **Note:** If you encounter `dpkg` lock issues (common on platforms like TensorDock), wait ~15 minutes and re-run the script. The script will **automatically reboot** the machine if a new NVIDIA driver was installed. | |
| #### Optional environment variables | |
| | Variable | Default | Description | | |
| |---|---|---| | |
| | `NVIDIA_DRIVER_VERSION` | `535` | NVIDIA driver version to install | | |
| Once the bootstrap completes (and any reboot finishes), proceed with the rest of this guide. | |
| --- | |
| ## Install PM2 (Process Manager) | |
| **PM2** is used to manage and monitor the validator process. If you haven’t installed PM2 yet, follow these steps: | |
| 1. Install `npm` and PM2: | |
| ```bash | |
| sudo apt update | |
| sudo apt install npm -y | |
| sudo npm install pm2 -g | |
| pm2 update | |
| ``` | |
| 2. For more details, refer to the [PM2 Documentation](https://pm2.io/docs/runtime/guide/installation/). | |
| --- | |
| ## Install Redis | |
| 1. Install 'redis' | |
| ```bash | |
| sudo apt update | |
| sudo apt install redis-server | |
| sudo systemctl start redis | |
| sudo systemctl enable redis-server | |
| sudo systemctl status redis | |
| ``` | |
| ## Install Project Dependencies | |
| ### Prerequisites | |
| - **Python**: Version 3.10 or higher | |
| - **pip**: Python package manager | |
| - **virtualenv** (optional): For dependency isolation | |
| --- | |
| ### 1. Clone the Repository | |
| Clone the project repository to your local machine: | |
| ```bash | |
| git clone https://github.com/vidaio-subnet/vidaio-subnet.git | |
| cd vidaio-subnet | |
| ``` | |
| --- | |
| ### 2. Set Up a Virtual Environment (Recommended) | |
| Create and activate a virtual environment to isolate project dependencies: | |
| ```bash | |
| python3 -m venv venv | |
| source venv/bin/activate | |
| ``` | |
| --- | |
| ### 3. Install the Package and Dependencies | |
| Install the project and its dependencies using `pip`: | |
| ```bash | |
| pip install -e . | |
| ``` | |
| --- | |
| ### 4. Configure Environment Variables | |
| To configure environment variables, follow these steps: | |
| 1. Create a `.env` file in the project root directory by referencing the provided `.env.template` file: | |
| ```bash | |
| cp .env.template .env | |
| ``` | |
| 2. Set up a bucket in cloud storage. The base miner code utilizes MinIO to connect with cloud storage services, so you'll need to prepare your bucket using a platform that supports MinIO integration, such as Backblaze. Alternatively, you can modify the code to suit your specific requirements. *IMPORTANT*: Note that currently the `region` of the storage is hardcoded, and must be adjusted in `vidaio_subnet_core/utilities/storage_client.py` for corresponding storage, such as AWS. | |
| 3. Add the required variables to the `.env` file. For example: | |
| ```env | |
| BUCKET_NAME="S3 buckent name" | |
| BUCKET_COMPATIBLE_ENDPOINT="S3 bucket endpoint" | |
| BUTKET_COMPATIBLE_ACCESS_KEY="S3 bucket personal access key" | |
| BUCKET_COMPATIBLE_SECRET_KEY="S3 bucket personal secret key" | |
| PEXELS_API_KEY="Your Pexels account api key" | |
| WANDB_API_KEY="Your WANDB account api key" | |
| ``` | |
| 4. Ensure that the bucket is configured with the appropriate permissions to allow file uploads and enable public access for downloads via presigned URLs. | |
| 5. Create your Pexels API key and replace it. (https://www.pexels.com/) | |
| 6. Once the `.env` file is properly configured, the application will use the specified credentials for S3 bucket, Pexels and Wandb. | |
| --- | |
| ## Install FFMPEG | |
| FFMPEG is required for processing video files. Install it using the following commands: | |
| ```bash | |
| sudo apt update | |
| sudo apt install ffmpeg -y | |
| ``` | |
| For more details, refer to the [FFMPEG Documentation](https://www.ffmpeg.org/download.html#build-linux). | |
| --- | |
| ## Install VMAF | |
| To enable video quality validation, install **VMAF** by following the steps below to set up a clean virtual environment, install dependencies, and compile the tool. | |
| --- | |
| Clone the VMAF repository into the working root directory of your `vidaio-subnet` package: | |
| ```bash | |
| git clone https://github.com/vidAio-subnet/vmaf.git | |
| cp vmaf_utils/Dockerfile vmaf/Dockerfile | |
| cp vmaf_utils/Dockerfile.ffmpeg vmaf/Dockerfile.ffmpeg | |
| cd vmaf | |
| git stash && git reset --hard 332dde62838d91d8b5216e9822de58851f2fd64f && git stash apply | |
| docker build -t vmaf . | |
| docker build -t vmaf_ffmpeg:latest -f Dockerfile.ffmpeg . | |
| ``` | |
| --- | |
| ## Competition scheduler and Modal execution | |
| Competition mode is independently disabled by default. With it disabled, the | |
| validator does not create competition tables, scan manifests, or change any | |
| synthetic/organic inference task. | |
| For each new competition, complete | |
| [`validator_competition_checklist.md`](validator_competition_checklist.md). | |
| The consolidated competition contract and timeout formulas are in | |
| [`competitions.md`](competitions.md). | |
| To prepare a competition scheduler, copy the example manifest from | |
| `competitions/manifests/examples/` into `competitions/manifests/`, update its | |
| ID, timestamps, and Volume names, and provide the stock warmup fixture. Then set: | |
| ```env | |
| COMPETITION_MODE_ENABLED=true | |
| COMPETITION_DATABASE_URL=sqlite:///video_subnet_validator.db | |
| COMPETITION_ARTIFACT_ROOT=competition_artifacts | |
| COMPETITION_MANIFEST_GLOB=competitions/manifests/*.json | |
| COMPETITION_SCHEDULER_INTERVAL_SECONDS=30 | |
| COMPETITION_LEASE_TTL_SECONDS=120 | |
| COMPETITION_NETWORK_TIMEOUT_SECONDS=30 | |
| COMPETITION_MAX_CONCURRENT_REQUESTS=32 | |
| METAGRAPH_REFRESH_INTERVAL_SECONDS=1800 | |
| COMPETITION_EXECUTION_ENABLED=true | |
| COMPETITION_BUILD_BACKEND=modal | |
| COMPETITION_ACCEPT_MODAL_BUILD_WITHOUT_SIZE_ATTESTATION=true | |
| COMPETITION_MODAL_ENVIRONMENT=main | |
| ``` | |
| The validator refreshes its in-memory metagraph every 1,800 seconds (30 | |
| minutes) by default. `METAGRAPH_REFRESH_INTERVAL_SECONDS` controls that cadence | |
| and refreshes all metagraph vectors, including the alpha-stake values used for | |
| competition enrollment. Submission finalisation uses the most recent snapshot | |
| recorded before the state transition when locking each submitted miner's | |
| alpha-stake eligibility; it does not trigger an additional refresh. | |
| `COMPETITION_DATABASE_URL` must be visible to both PM2 validator processes. | |
| The competition-mode process writes scores and final rankings to that database; | |
| the inference-mode process opens the same database only as its competition | |
| ranking source when composing on-chain weights. Its inference performance data | |
| continues to use `video_subnet_validator.db`. If the variable is absent from the | |
| inference process, competition rewards are disabled and weights retain the | |
| 80% compression / 20% upscaling inference fallback. Restart both processes with | |
| updated environment variables after changing the URL. | |
| The four execution settings above enable the validator-owned Modal build and | |
| Sandbox path in testnet or production. `modal` builds each pinned contender | |
| repository directly on Modal, records its immutable image ID as | |
| `MODAL_ACCEPTED`, and starts its validator-controlled offline Sandbox. Builds | |
| and Sandbox startup run concurrently, bounded by the manifest's | |
| `max_parallel_contenders` value. | |
| The pre-evaluation submission archive contains every pinned GitHub repository, | |
| the manifest-configured boss repository (when configured), and an online, | |
| integrity-checked SQLite snapshot under `database/<sqlite-filename>`. The SQLite | |
| source path and filename come from `COMPETITION_DATABASE_URL`. Backup logs print | |
| the complete S3 object names for both the archive and its `inventory.json`. | |
| The logged `database_archive_path` is the SQLite member name inside the tarball, | |
| not a standalone S3 key and not a secret. Keep the archive and inventory | |
| private because both contain sensitive competition material. | |
| The backup privacy gate checks bucket and object ACLs and rejects wildcard | |
| public-read statements when the provider exposes bucket policy. Native AWS S3 | |
| must enable all four Public Access Block controls. A custom S3-compatible | |
| endpoint such as Backblaze B2 may report those AWS-specific controls as absent | |
| or disabled; the validator warns but does not reject the bucket when its | |
| supported bucket/object privacy checks pass. Configure the B2 bucket as private | |
| so objects require the configured application key ID and application key. | |
| Competition-mode startup runs this bucket privacy preflight before enrollment | |
| or scheduler work begins and exits on a detected violation. The same check runs | |
| again at upload time so a policy change during enrollment still fails closed. | |
| When `competition_end_time` moves a competition to `COMPLETED`, the validator | |
| also creates a final online, integrity-checked SQLite snapshot and uploads it | |
| privately to | |
| `s3://<bucket>/<artifact-prefix>/<competition-id>/final/<manifest-digest>/<sqlite-filename>`. | |
| The source path and filename come from `COMPETITION_DATABASE_URL`. The object | |
| size and SHA-256 are verified after upload, and a successful upload is recorded | |
| in the audit event stream. Failed uploads are logged and retried on later | |
| scheduler ticks. | |
| For a manifest-configured boss, the pre-evaluation artifact archive contains | |
| the immutable boss export as `contenders/<boss_hotkey>-boss` and any submission | |
| from the same hotkey as `contenders/<boss_hotkey>`. Both are scored; only the | |
| higher-scoring row is ranked. The final SQLite snapshot preserves `is_boss` and | |
| the concrete contender ID, so it identifies which solution won even though | |
| `competitions.winner_hotkey` remains the real payout hotkey. | |
| Before enrollment, validate the boss configuration and exact SDK export selected | |
| by the manifest: | |
| ```bash | |
| python scripts/competition_preflight.py \ | |
| competitions/manifests/compression-YYYY-wWW.json \ | |
| --validate-boss | |
| ``` | |
| The command must report `ACCEPTED`. It fails non-zero when the boss is missing | |
| from the manifest, its path escapes the validator checkout, the export is | |
| incomplete, or the normal objective static source policy returns | |
| `REVIEW_REQUIRED` or `REJECTED`. | |
| Modal does not currently provide final-image size attestation for this build | |
| path. Setting | |
| `COMPETITION_ACCEPT_MODAL_BUILD_WITHOUT_SIZE_ATTESTATION=true` is an explicit | |
| operator acknowledgement of that limitation; it is not a claim that the 25 GB | |
| limit was measured. Deployments that require attested size enforcement can | |
| still integrate the stricter trusted-builder interface. | |
| Every direct Modal contender build uses the manifest's `modal_build_timeout` | |
| duration, defaulting to `10m` when that field is omitted. Builds run in separate | |
| client processes and use contender-specific Modal Apps, so one stalled or | |
| malicious Dockerfile cannot block another contender's parallel build. When the | |
| deadline is exceeded, the validator kills the build client, asks Modal to stop | |
| that isolated App, records the contender as rejected with `BUILD_TIMEOUT`, and | |
| continues the `BUILDING` phase. Remote App cancellation is best-effort, so | |
| operators should investigate any App that Modal still reports as active after a | |
| `BUILD_TIMEOUT` event. | |
| ### Modal authentication for competition execution | |
| The scheduler and enrollment path above do not need Modal credentials. The | |
| validator-owned execution path does: it creates Modal Sandboxes, looks up the | |
| manifest's input Volume, and creates a separate output Volume for each | |
| contender. The Modal SDK is installed by `pip install -e .` because it is listed | |
| in `requirements.txt`. | |
| Contender output Volumes are created automatically as Modal v2 Volumes. V2 is | |
| required because the validator explicitly commits each completed batch with | |
| `sync` before reading it; this Sandbox commit operation is unavailable for v1 | |
| Volumes. Do not pre-create generated output Volumes with Modal's default v1 | |
| setting. | |
| Generated names retain a bounded namespace from `output_volume_prefix`, the | |
| actual `competition_id`, and recognizable leading/trailing characters from the | |
| miner hotkey plus a collision-resistant suffix. For example: | |
| ```text | |
| vidaio-compression-2026-w30-5FRUjvY1AxpK-bLqFmJLo-bad385 | |
| ``` | |
| The database remains authoritative for the complete hotkey-to-Volume mapping. | |
| Contender code only performs the requested transformation; it is not trusted to | |
| validate or score its output. Every returned output is committed to the | |
| contender's output Volume and independently inspected by the validator. A | |
| competition result contains only its validator-assigned `output_path` (or | |
| `null` when no output was produced); it cannot supply success, failure reason, | |
| timing, evaluation identity, hotkey, or batch identity. Results are matched to | |
| request items by position and exact output path. A valid file is scored | |
| normally; an invalid file is `FAILED` with zero score while retaining any | |
| measurable size, compression, VMAF, validator-measured runtime, and cost | |
| metrics. Missing outputs remain ordinary failures with no stored file. | |
| As soon as one contender has terminal results for every evaluation item, the | |
| validator terminates that contender's Sandbox without waiting for slower miners. | |
| Later scheduler ticks exclude completed contenders instead of warming them | |
| again. This does not delete the input Volume, contender output Volumes, or any | |
| files stored in them. Competition Volumes persist until an operator manually | |
| deletes them. | |
| If an empty v1 output Volume already exists under a generated contender name, | |
| stop the validator and any Modal Apps using it, then replace it explicitly: | |
| ```bash | |
| modal volume delete --env main --yes "$OUTPUT_VOLUME" | |
| modal volume create --env main --version 2 "$OUTPUT_VOLUME" | |
| ``` | |
| Never delete a non-empty output Volume as an automatic repair. Modal does not | |
| provide an in-place v1-to-v2 upgrade; migrate data that must be retained to a | |
| separately named v2 Volume first. | |
| Authenticate the operating-system user that runs the validator: | |
| ```bash | |
| modal setup | |
| ``` | |
| For a headless host or a validator managed by PM2, provide a Modal token in the | |
| validator process environment instead. Do not put this token in a miner | |
| submission or expose it to a contender Sandbox. | |
| ```env | |
| MODAL_TOKEN_ID=your-modal-token-id | |
| MODAL_TOKEN_SECRET=your-modal-token-secret | |
| ``` | |
| Make sure the selected Modal environment exists and contains the read-only | |
| input Volume named by `evaluation_input_volume_name` in the competition | |
| manifest. The validator uses the environment selected by | |
| `COMPETITION_MODAL_ENVIRONMENT` (`dev` by default). Its launch policy passes | |
| `secrets=[]`, disables OIDC identity, and | |
| blocks outbound networking, so the validator's Modal token remains in the | |
| trusted host process rather than inside contender code. | |
| For example, create the manifest's input Volume before starting the validator: | |
| ```bash | |
| INPUT_VOLUME=vidaio-competition-compression-2026-w30-inputs | |
| modal volume create \ | |
| --env main \ | |
| "$INPUT_VOLUME" | |
| ``` | |
| `INPUT_VOLUME` must exactly match the manifest's | |
| `evaluation_input_volume_name`. | |
| Creating the Volume does **not** upload the evaluation dataset. An empty Volume | |
| causes the validator to remain in `EVALUATING` while its Sandboxes continue to | |
| answer `/health`. Phase 4 dispatch starts only after the Volume contains a | |
| read-back-verified index and source files and that exact index digest is sealed | |
| in the competition database. | |
| ### Prepare, validate, upload, and seal the Phase 4 dataset | |
| Choose the manifest that the validator will load, a private directory containing | |
| the source MP4 files, a temporary local index path, and the validator's actual | |
| SQLite URL: | |
| ```bash | |
| MANIFEST=/absolute/path/to/competitions/manifests/compression-2026-w30.json | |
| SOURCE_DIR=/absolute/path/to/private-evaluation-videos | |
| INDEX=/tmp/compression-2026-w30-index.json | |
| DATABASE_URL=sqlite:////absolute/path/to/video_subnet_validator.db | |
| ``` | |
| Run all four operations, in this exact order, from the same deployed repository | |
| and virtual environment as the validator. The CLI enforces the sequence: | |
| ```bash | |
| python scripts/competition_dataset.py prepare \ | |
| --manifest "$MANIFEST" \ | |
| --source-dir "$SOURCE_DIR" \ | |
| --index "$INDEX" | |
| python scripts/competition_dataset.py validate \ | |
| --manifest "$MANIFEST" \ | |
| --source-dir "$SOURCE_DIR" \ | |
| --index "$INDEX" | |
| python scripts/competition_dataset.py upload \ | |
| --manifest "$MANIFEST" \ | |
| --source-dir "$SOURCE_DIR" \ | |
| --index "$INDEX" \ | |
| --environment main | |
| python scripts/competition_dataset.py seal \ | |
| --manifest "$MANIFEST" \ | |
| --index "$INDEX" \ | |
| --environment main \ | |
| --database-url "$DATABASE_URL" | |
| ``` | |
| `prepare` probes every MP4 and records immutable size, checksum, duration, | |
| dimensions, frame count, codec, pixel format, and aspect ratio metadata. | |
| Each source becomes exactly one compression evaluation row. Its CRF or VBR mode | |
| and VMAF target (85, 89, or 93) are selected by deterministic pseudorandom choice | |
| seeded from the manifest; VBR queries also receive a 5, 8, or 10 Mbps target. | |
| Rebuilding an unchanged index therefore produces the same query set. A full | |
| five-item batch always contains five distinct physical videos. | |
| `prepare` also writes `<index>.pipeline.json`, binding the manifest and index | |
| digests to the source directory and file identities. `validate` checks that | |
| receipt, the index contract, and that the prepared files remain unchanged; it | |
| does not repeat the full hashing and probing already performed by `prepare`. | |
| Keep the receipt beside the index through sealing. `prepare` places the canonical | |
| batch prefix in each indexed source path. `upload` requires the validated receipt | |
| and writes the trusted index and only the | |
| immutable canonical-batch source trees to the manifest's input Volume, then | |
| reads back every indexed source to verify its checksum. It does not create a | |
| duplicate root `inputs/` tree. | |
| It refuses to replace a different index. Successful read-back advances the | |
| receipt to `uploaded`. `seal` requires that receipt and verifies the remote/local | |
| index digest before writing immutable evaluation rows to SQLite; it does not | |
| download and probe every verified video again. An upload verification failure | |
| requires a new competition ID and Volume; | |
| the uploaded index and batch trees are never narrowed or replaced. | |
| If the database does not exist yet, `seal` creates it, applies the competition | |
| migrations, and registers the manifest in `SCHEDULED` state. If the competition | |
| already exists, it requires the database manifest digest to match and therefore | |
| cannot silently replace the validator's configured revision. | |
| Because the sealed index and contender API contract are immutable for a live | |
| competition, use a new competition ID for this one-query-per-video format and | |
| require miners to republish with the matching SDK (`--refresh` when upgrading | |
| an existing prepared repository). | |
| This means the entire sequence can run while the validator is stopped. On its | |
| first boot the validator finds the pre-registered, pre-sealed competition and | |
| starts it automatically when `competition_start_time` is due. Keep the manifest | |
| file in `COMPETITION_MANIFEST_GLOB` and point `COMPETITION_DATABASE_URL` at the | |
| same database passed to `seal`. | |
| Use an absolute SQLite URL as shown above, or prove that the CLI and PM2 use the | |
| same working directory. Sealing a different relative database file will leave | |
| the running validator waiting for a dataset even though the CLI reported | |
| success. Preloading is recommended, but not mandatory: the validator may also | |
| start with an empty Volume and wait in `EVALUATING`; completing the mandatory | |
| four-stage pipeline later unblocks it on the next execution cycle. After late | |
| sealing, restart or wait for that cycle: | |
| ```bash | |
| PM2_APP=video-validator-testnet | |
| pm2 restart "$PM2_APP" --update-env | |
| ``` | |
| Expected progress logs include `Dispatching competition batch`, `Competition | |
| batch persisted`, `EVALUATING -> SCORING`, `SCORING -> AWAITING_END_TIME`, and | |
| `Competition Phase 4 complete`. Each contender logs `Contender Sandbox | |
| terminated after evaluation` with `trigger=contender_dataset_complete` and | |
| `volume_retained=true` as soon as its own work is done. Dataset | |
| checksum mismatches and missing outputs remain fail-closed in `EVALUATING` with | |
| a reason-coded log. See | |
| [`competition_phase4_dataset_runbook.md`](competition_phase4_dataset_runbook.md) | |
| for the focused operator procedure. | |
| Competition execution and scoring deadlines are derived from each batch's | |
| sealed media metadata. Encoding is modeled as four parallel video lanes, with | |
| each video split into 10-minute chunks. A 10-minute 4K chunk takes two minutes; | |
| every started chunk receives that full allowance, including videos shorter than | |
| 10 minutes and final partial chunks. Every video also has an absolute two-minute | |
| processing floor before parallel scheduling, so resolution scaling cannot reduce | |
| a short video below that allowance. Work above the floor scales linearly with | |
| pixel count. VMAF scoring is modeled sequentially at 200 FPS for 4K and also | |
| scales with pixel count. Both processing deadlines include two minutes for | |
| Volume I/O and startup, and the database execution lease includes another two | |
| minutes for validator recovery and outcome persistence. The Sandbox row records | |
| the most recently applied invocation deadline, while each batch row records its | |
| attempt-specific execution lease and scoring deadline. | |
| The manifest's `evaluation_batched_run_timeout` and | |
| `scoring_batched_run_timeout` are minimum deadlines, not fixed estimates. | |
| Dynamic workload calculations may increase them but never reduce them. The full | |
| minimum also applies to partial final or explicitly operator-requeued batches | |
| and is not prorated by item count. See | |
| [`competitions.md`](competitions.md#batch-and-timeout-fields) for the formulas, | |
| persistence semantics, and worked examples. | |
| The validator creates every `/output/evaluations/<batch-id>` directory before | |
| calling the contender's `/compress` route. A Sandbox request failure is logged | |
| with its reason code and redacted detail; the persisted summary reports the | |
| actual post-attempt status and a per-reason count. If any current terminal item | |
| failed for a validator-infrastructure reason, evaluation stays fail-closed in | |
| `EVALUATING` instead of applying that failure to a contender's score. A vanished | |
| Sandbox is detected by polling its lifecycle independently from the request | |
| process, so control-plane delay does not inflate recorded runtime to the full | |
| invocation deadline. | |
| Sandbox creation, isolation, and readiness are a pre-dispatch gate: failure to | |
| warm any eligible contender pauses dispatch before claims are created, so a | |
| startup outage does not consume evaluation attempts. Startup and recovery | |
| details are also retained in the redacted `SANDBOX_CLOSED` audit event. | |
| After fixing validator infrastructure, an operator can requeue those exhausted | |
| attempts without deleting their history. Stop the validator first so the repair | |
| does not race its scheduler, use the same absolute SQLite URL, then restart it: | |
| ```bash | |
| python scripts/competition_repair.py requeue-infrastructure \ | |
| --competition-id compression-2026-w30 \ | |
| --database-url "$DATABASE_URL" \ | |
| --reason-code SANDBOX_START_FAILED \ | |
| --apply | |
| pm2 restart "$PM2_APP" --update-env | |
| ``` | |
| For a Sandbox that exits during an active batch, use | |
| `--reason-code SANDBOX_DISAPPEARED`. Inspect the Modal lifecycle event and exit | |
| code first; the repair is for validator/platform loss, not a reproducible | |
| contender-controlled crash. | |
| The repair accepts only allowlisted infrastructure reason codes, records an | |
| `EVALUATION_INFRASTRUCTURE_REQUEUED` audit event, retains old attempts as | |
| `REQUEUED`, and returns an `AWAITING_END_TIME` competition to `EVALUATING`. | |
| New attempts continue with increasing attempt numbers. | |
| Before enabling competition execution, verify authentication and the | |
| offline Sandbox policy with the bounded live probe (this creates a short-lived | |
| Modal Sandbox and may incur a small charge): | |
| ```bash | |
| python scripts/competition_modal_phase0_probe.py \ | |
| --environment main \ | |
| --skip-billing | |
| ``` | |
| Omit `--skip-billing` only when validating billing-report capability. Billing | |
| reports require a supported Modal Team or Enterprise workspace; they are not | |
| required for the network-isolation probe. | |
| > **Current implementation status:** the live validator now orchestrates | |
| > submission finalisation, static-validation acceptance, image building, | |
| > isolated Sandbox startup, durable evaluation dispatch, output validation, | |
| > VMAF and cost scoring, and the transition to `AWAITING_END_TIME`. Batches are | |
| > sequential per warm contender while different contenders run concurrently up | |
| > to `max_parallel_contenders`. SQLite is authoritative for claims, lease | |
| > expiry, terminal failure persistence, and exact-once result persistence; | |
| > Redis is not required for recovery. Sandbox-forward, storage/scoring, and | |
| > lease-expiry failures are not automatically retried. Existing | |
| > `DEVELOPMENT_ACCEPTED` rows remain readable for migration. | |
| Competition cost scoring locks Modal's public rates as of 2026-07-21: B300 | |
| `$0.001972/s`, B200 `$0.001736/s`, H200 `$0.001261/s`, H100 `$0.001097/s`, RTX | |
| PRO 6000 `$0.000842/s`, A100 80 GB `$0.000694/s`, A100 40 GB `$0.000583/s`, | |
| L40S `$0.000542/s`, A10 `$0.000306/s`, L4 `$0.000222/s`, and T4 `$0.000164/s`. | |
| Modal's separate Sandbox CPU rate is `$0.00003942/physical-core/s`. The validator | |
| probes the GPU model/count and CPU allocation inside every running Sandbox, | |
| persists them separately from the request, and estimates the full batch wall time | |
| from that observed allocation. Batch `active_runtime_seconds` is | |
| therefore equal to `wall_runtime_seconds`; failed validation, failed scoring, | |
| and other terminally failed items still consumed the Sandbox and retain their | |
| equal share of runtime and estimated cost. Automatic evaluation replay is | |
| disabled, preventing a contender from deliberately failing long videos and | |
| processing them later to alter the normal cost aggregate. Per-item compression | |
| ratio, VMAF, checksums, and media sizes are retained on failed rows whenever | |
| those measurements could be completed safely. A metric remains null only when | |
| the output could not be read, probed, or compared sufficiently to produce it. It | |
| does not attempt to reproduce the final Modal invoice: memory above the request, | |
| image builds, Volume storage, credits, reservations, taxes, CPU utilization | |
| reconciliation, and unrelated workspace usage are excluded. Some Modal | |
| automatic GPU upgrades retain the requested SKU's invoice rate; invoice-aligned | |
| data belongs in | |
| `reconciled_cost_usd`, while competition estimates consistently price the | |
| observed hardware. | |
| Competition scoring version 3 keeps each item's media score absolute: it comes | |
| directly from `services/scoring/scoring_function.py` using its measured | |
| compression rate, measured VMAF, and sealed VMAF target. Cost efficiency is | |
| relative for each evaluation item. The cheapest valid contender receives 1.0 | |
| and every other valid contender receives `minimum_valid_cost / own_cost`. | |
| Failed and invalid outputs receive zero and cannot define the minimum. Because | |
| cost is population-dependent, aggregate scoring must use the complete finalized | |
| evaluated contender set. | |
| The start time may fall on any day of the week. At startup the validator | |
| normalizes and hashes each manifest and persists it in competition-only SQLite | |
| tables. If a | |
| manifest with the same ID changes while its competition is non-terminal, the | |
| validator updates the database fields used by the scheduler, appends a | |
| `MANIFEST_UPDATED` audit event containing the old/new digests and field-level | |
| changes, archives the prior normalized manifest below | |
| `competition_artifacts/<competition-id>/manifest.revisions/`, and makes the new | |
| normalized manifest current. Completed, failed, and cancelled competitions stay | |
| immutable and require a new competition ID. | |
| If SQLite and `manifest.normalized.json` contain different revisions after an | |
| interrupted update, SQLite remains authoritative. The validator preserves the | |
| divergent file as `manifest.revisions/observed-<sha256>.json`, appends a | |
| `MANIFEST_ARTIFACT_DIVERGENCE` audit event with the observed/database/requested | |
| digests, and atomically restores the requested manifest as current. | |
| Startup fails closed if the configured warmup fixture is absent. The control | |
| scheduler advances persisted lifecycle state. If both the edited start and | |
| contender-finalisation times are already past, one scheduler tick advances the | |
| competition through `ENROLLING` to `FINALIZING_SUBMISSIONS`. No invitations are | |
| sent in that case because the collection window has already closed. Otherwise, | |
| on entry to `ENROLLING`, the validator snapshots serving miner axons, | |
| sends each miner a competition invitation, and immediately polls opt-ins for a | |
| submission. `NOT_READY` miners are polled again at `contender_ping_interval` | |
| until `contender_finalisation_time`; invitation and polling attempts survive | |
| validator restarts. A `READY` response is cloned and pinned immediately so the | |
| raw repository credential is never persisted. Trusted builds and evaluation | |
| remain separate fail-closed lifecycle gates. | |
| Only `COMPRESSION` manifests are active today. The code reserves a future | |
| upscaling media contract with three distinct roles: (i) high-resolution ground | |
| truth held for trusted scoring, (ii) a downsampled reference mounted as the | |
| miner input, and (iii) the miner-processed output. The upscaling adapter remains | |
| an explicit `NotImplementedError` stub; do not configure or advertise an | |
| upscaling competition until its manifest, route, isolation policy, and scorer | |
| are implemented and tested. | |
| Keep `COMPETITION_MODE_ENABLED=false` in production unless the outstanding | |
| trusted-builder Phase 0 gate is satisfied or the operator has explicitly | |
| accepted the unattested direct-Modal build limitation described above. | |
| ## Running the Validator with PM2 | |
| To run the validator, use the following command: | |
| ```bash | |
| pm2 start run.sh --name vidaio_v_autoupdater -- --wallet.name [Your_Wallet_Name] --wallet.hotkey [Your_Hotkey_Name] --subtensor.network finney --netuid 85 --axon.port [port] --logging.debug | |
| ``` | |
| ### Parameters: | |
| - **`--wallet.name`**: Replace `[Your_Wallet_Name]` with your wallet name. | |
| - **`--wallet.hotkey`**: Replace `[Your_Hotkey_Name]` with your hotkey name. | |
| - **`--subtensor.network`**: Specify the target network (e.g., `finney`). | |
| - **`--netuid`**: Specify the network UID (e.g., `85`). | |
| - **`--axon.port`**: Replace `[port]` with the desired port number. | |
| - **`--logging.debug`**: Enables debug-level logging for detailed output. | |
| --- | |