How to run basecrawl as a local miner binary, pull the digest-pinned CVM image from GHCR, and place it on a Phala / dstack TDX guest. Companion docs: Install & publish, Proxy & egress, Breadth & extract, Security, Trust model, Image rotation on CVE, TCB inventory.
This guide is operator procedure, not a claim of absolute authenticity, anonymity, or defeat of every bot vendor. Authenticity remains cryptographically-anchored trust-but-audit.
| Path | When to use | Attestation |
|---|---|---|
| Local CLI / binary | Miners and operators developing or scraping without TEE | No TDX quote; fail closed if you pass --attest outside a CVM |
| GHCR image on host Docker | Smoke the product image without Phala | No guest dstack socket unless you mount one |
| Phala TDX CVM (dstack) | Production attested scrapes for validators / relay | L1 allowlist + L2 report_data via /var/run/dstack.sock |
flowchart TB
Op[Operator]
Local[Local cargo binary]
GHCR[ghcr.io/baseintelligence/basecrawl-cvm]
Host[Host Docker poke]
Phala[Phala TDX / dstack 0.5.9]
Sock["/var/run/dstack.sock"]
Proof[ScrapeProof JSON]
Op --> Local
Op --> GHCR
GHCR --> Host
GHCR --> Phala
Phala --> Sock
Local --> Proof
Host --> Proof
Sock --> Proof
Use this path for soft scrapes, hermetic tests, proxy wiring, and optional CapSolver without a CVM.
Rust toolchain is pinned in rust-toolchain.toml (1.96.0).
Preferred install after a public crates.io release (thin packaging crate):
cargo install basecrawl --lockedFrom the repository root (pre-publish, patches, or local engineering):
cargo build --release --locked --package basecrawl-core --bin basecrawl
# binary: target/release/basecrawl
# PATH install from monorepo path (thin crate preferred):
cargo install --path crates/basecrawl --locked
# alternate:
cargo install --path crates/basecrawl-core --locked --bin basecrawlPublished package list, Node @basecrawl/sdk linux-x64 residual, Chromium residual, and the
tag v* β publish.yml release story: install-and-publish.md.
./target/release/basecrawl \
--formats markdown,metadata \
--timeout 60 \
https://example.com/Stdout is exactly one ScrapeProof JSON object on success. Failures write {"error": ...} to
stderr and exit non-zero (no partial truth on stdout).
Secrets stay in a gitignored .env (mode 600). Never paste passwords into shells, tickets, or
this doc.
# .env (mode 600) β names only; set real values offline
# BASECRAWL_HTTPS_PROXY=http://user:pass@provider-host:port
# BASECRAWL_HTTP_PROXY=http://user:pass@provider-host:port
set -a; . ./.env; set +a
./target/release/basecrawl \
--proxy-class residential \
--proxy-session s1 \
--proxy-country US \
--formats markdown,metadata \
https://example.com/- Universal proxy: any
http(s)://user:pass@host:portCONNECT orsocks5://β¦URL. - Oxylabs is a live residential fixture, not a hard product dependency. Prefer sticky country templates documented in proxy-and-egress.md.
- Live commercial dialing: keep max 1 concurrent residential family when
BASECRAWL_LIVE_PROXY=1is set. Proxy class honesty is fail-closed (no fake residential on a direct dial). - Full flag table and composer behavior: proxy-and-egress.md.
Default challenge posture is detect-not-solve (challenge_blocked without unlocking forged
content). CapSolver is optional:
# .env (mode 600)
# CAPSOLVER_API_KEY=β¦ # or BASECRAWL_CAPSOLVER_API_KEY
# BASECRAWL_CAPTCHA_SOLVER=capsolver
./target/release/basecrawl \
--captcha-solver capsolver \
--formats markdown,metadata \
--force-browser \
https://example.com/Without a key or provider selection the product stays detect-only. CapSolver does not equal commercial Web Unlocker parity. Details: proxy-and-egress.md.
./target/release/basecrawl --attest https://example.com/
# fails closed: no fabricated TDX quote outside /var/run/dstack.sockPrimary auto-publish path (GitHub Actions Image workflow):
ghcr.io/baseintelligence/basecrawl-cvm
| Tag / pin style | Use |
|---|---|
@sha256:<digest> |
Required for verification / allowlist / production Phala pin |
sha-<full-git-sha> |
Immutable commit tag from Actions |
sha-<short> |
Convenience immutable short form |
:latest |
Convenience only on default-branch publishes; do not use for verification |
Public pulls may work after package visibility is set. For private packages or rate limits:
# GitHub CLI (preferred when logged in)
gh auth token | docker login ghcr.io -u USERNAME --password-stdin
# Or PAT with read:packages (never commit the token)
echo "$GITHUB_TOKEN" | docker login ghcr.io -u USERNAME --password-stdin# Prefer an immutable tag from Actions, then re-pin by digest for compose
docker pull ghcr.io/baseintelligence/basecrawl-cvm:sha-<full-git-sha>
# Or pull latest for a quick poke only (not for verification)
docker pull ghcr.io/baseintelligence/basecrawl-cvm:latest
# Resolve the content digest for allowlists / compose pin
docker image inspect ghcr.io/baseintelligence/basecrawl-cvm:sha-<full-git-sha> \
--format '{{index .RepoDigests 0}}'Digest pin form used everywhere verification matters:
ghcr.io/baseintelligence/basecrawl-cvm@sha256:<digest>
Rebuild policy and allowlist rotation: image-rotation-on-cve.md.
docker run --rm \
ghcr.io/baseintelligence/basecrawl-cvm@sha256:<digest> \
--helpAttested scrapes still need a dstack guest agent socket (next section).
| Artifact | Path / URL |
|---|---|
| Dockerfile | image/Dockerfile |
| Example compose | image/docker-compose.yml |
| Actions workflow | .github/workflows/image.yml |
| Cargo quality gate | .github/workflows/ci.yml (fmt / clippy / tests; unchanged by image publish) |
| Image workflow runs | Actions β Image |
Historic alternate pin (may lag GHCR auto path; still digest-only for verification):
docker.io/mathiiss/basecrawl-cvm@sha256:<digest>
GHCR is the primary auto-publish target (packages: write + GITHUB_TOKEN on main /
workflow_dispatch). Cargo gate CI remains independent of registry software.
Production attestation uses Phala managed TDX and guest OS dstack 0.5.9 family
(slug family dstack-0.5.9-bd369a8c). The guest must expose:
/var/run/dstack.sock
used by Info, GetQuote, and related dstack endpoints. Outside that socket, --attest fails closed.
- Phala account / CLI authenticated (
phala statussucceeds). Never print API keys. - Capacity for a linux/amd64 TDX guest using product dstack
0.5.9(not a floating OS tag). - An immutable image digest (
ghcr.io/baseintelligence/basecrawl-cvm@sha256:β¦, or the documented docker.io alternate pin after you verified it matches the intended measurement). - Validator / key-release measurement allowlist path ready for rotation when remapping measurements (image-rotation-on-cve.md).
Edit image/docker-compose.yml (or your operator-owned compose
fork) so services.basecrawl.image is digest-pinned:
services:
basecrawl:
image: "ghcr.io/baseintelligence/basecrawl-cvm@sha256:<YOUR_VERIFIED_DIGEST>"
# β¦ entrypoint binds /var/run/dstack.sock and runs the attested scrape command β¦
volumes:
- type: "bind"
source: "/var/run/dstack.sock"
target: "/var/run/dstack.sock"Rules:
- Prefer
@sha256:β¦over any floating tag for production and allowlists. - After changing the image line, recompute compose_hash / measurement tuples offline with the
pinned
dstack-mrtooling described in tcb-inventory.md and image-rotation-on-cve.md. - Product app-compose examples also live under
image/phala-app-compose.json(features includekms/ gateway surface as configured for Phala).
- Select digest from a successful Image Actions run
(
sha-<gitsha>tag β content digest) or from a local reproducible rebuild pair (python3 image/reproducibility.pypath). - Re-pin
image/docker-compose.yml(and Phala app-compose if you embed compose text) toghcr.io/baseintelligence/basecrawl-cvm@sha256:<digest>. - Allow GHCR pull for the CVM pre-launch path if the package is private: supply registry pull
credentials through Phala/dstack secure env surfaces only (
DSTACK_DOCKER_*style). Never commit registry passwords. Public packages skip this login. - Create the CVM with Phala CLI / console on dstack
0.5.9, TDX instance class, compose fromimage/,kms_type: phala(product placement). Prefer automatic placement only when capacity documentation for your account allows it. - Wait for
/var/run/dstack.sockand a healthy guest pull of the pinned digest. - Run an attested scrape inside the guest (example pattern already baked into product compose for readiness):
basecrawl --attest \
--task-id YOUR-TASK \
--nonce YOUR-NONCE \
--formats markdown,metadata,rawHtml \
--timeout 60 \
https://example.com/- Verify offline with
dcap-qvl(quote verify/decode) and check L1 measurement against the current allowlist (image/allowlist.json/ relay platform allowlist file). L2 must bindreport_datahashes as documented in architecture.md and TRUST_MODEL.md. - On Chromium/OS CVE or deliberate TCB change: rebuild, publish new immutable digest,
path-rotate allowlist (new in, old out). Follow
image-rotation-on-cve.md exactly; never float
:latestas the verification pin and never leave vulnerable measurements accepted by default indefinitely. - Tear down mission/test CVMs when finished. Do not leave orphaned billed guests.
A verifying quote matching an allowlisted measurement is strong evidence the scrape ran in the pinned image with bound hashes. It is not absolute authenticity: residual risk includes TEE.fail on self-hosted DDR5, measured-but-exploited guest software, proxy β anonymity, and headless/CDP detector residual. See SECURITY.md.
Values never belong in git, ScrapeProof, measurements, CI logs, or this table. Use a gitignored
.env (mode 600) or your platform secret store.
| Name | Purpose |
|---|---|
BASECRAWL_HTTP_PROXY |
Soft/hard ambient HTTP CONNECT / proxy URL |
BASECRAWL_HTTPS_PROXY |
Soft/hard ambient HTTPS proxy URL (preferred for HTTPS origins) |
HTTP_PROXY / HTTPS_PROXY / ALL_PROXY |
Standard ambient proxies (product resolution when BASECRAWL_* unset) |
BASECRAWL_LIVE_PROXY |
1 arms live residential dials (max 1 concurrent family) |
OXYLABS_USERNAME / OXYLABS_PASSWORD |
Optional Oxylabs credential split if your operator scripts use them (prefer full proxy URL form) |
CAPSOLVER_API_KEY |
Optional CapSolver client key |
BASECRAWL_CAPSOLVER_API_KEY |
Optional CapSolver alias under the BASECRAWL_* namespace |
BASECRAWL_CAPTCHA_SOLVER |
capsolver when the optional solver is selected |
BASECRAWL_CAPSOLVER_API_BASE |
Optional CapSolver API base override (defaults to product endpoint) |
BASECRAWL_EXTRACT_API_KEY |
Optional structured extract provider key |
OPENAI_API_KEY |
Optional extract provider key (alias surface) |
FIRECRAWL_API_KEY |
Benchmark / comparison harness only; never required for scrapes |
GITHUB_TOKEN / GHCR deploy token |
Actions image publish uses GITHUB_TOKEN with packages: write; operator pulls use read:packages PAT or gh when private |
CARGO_REGISTRY_TOKEN |
Tag-driven crates.io publish secret name only (see install-and-publish); never commit values |
NPM_TOKEN |
Optional @basecrawl/sdk residual npm publish secret name only; never commit values |
| Phala CLI profile / API key | Phala account auth for CVM lifecycle; use local CLI profile; never commit |
DSTACK_DOCKER_USERNAME / DSTACK_DOCKER_PASSWORD / DSTACK_DOCKER_REGISTRY |
Guest pre-launch registry login when pulling private packages into the CVM |
CHALLENGE_MEASUREMENT_ALLOWLIST_FILE / CHALLENGE_KEY_RELEASE_ALLOWLIST_FILE |
Validator-side allowlist paths (relay / challenge host), not crawler secrets |
Redact credentials to username + *** at most in logs. Product proof and attestation surfaces never
include proxy passwords or CapSolver keys.
| Residual | Guidance |
|---|---|
| Authenticity | Cryptographically-anchored trust-but-audit; not absolute certainty |
| Proxy / residential | Topology and success-rate tool; not anonymity |
| CapSolver | Optional Turnstile path; not Web Unlocker parity; default is detect-not-solve |
| Headless Chromium | Hard-path identity baseline only; host and CVM need Chromium/Chrome; detector residual remains |
| Soft TLS vs hard path | Soft chrome-TLS impersonate is not Chromium document TLS |
npm @basecrawl/sdk |
Published package line is linux-x64 only; not multi-OS without multi-arch artifacts |
| TEE.fail / hardware | Self-hosted DDR5 residual; managed Phala reduces but does not erase all TEE risk |
| Image tags | :latest is convenience; digests pin verification and allowlists |
| Registry releases | Tags v* drive publish.yml; secrets by name only |
Product claims must never use absolute-trust wording. The following are forbidden claims (not claims this product makes): absolute-trust "trustless" language, "100%" authenticity, "guaranteed" unlock, "anonymous" egress, and "undetectable" browsing.
| Topic | Link |
|---|---|
Install / crates / npm / v* publish |
install-and-publish.md |
| Proxy / CapSolver / Oxylabs | proxy-and-egress.md |
| POST / crawl / map / batch / extract | product-breadth-and-extract.md |
| CVE image rotation | image-rotation-on-cve.md |
| Security residuals | SECURITY.md |
| Trust model | TRUST_MODEL.md |
| Publish workflow | Actions β Publish |
| Image workflow | Actions β Image |
| Cargo quality workflow | Actions β CI |