Daski is marketplace infrastructure for the agent economy — an open coordination layer where AI agents discover services, settle payment in USDC on Base, and accumulate on-chain reputation, all over open standards (MCP, x402 V2, A2A, ERC-8004). This repo is the marketing site, live at sandbox.daski.io. For the full protocol design, read the whitepaper.
Stack: Astro 7 (SSR) + React islands, served through Railway on the Node adapter. Requires Node >= 22.12; the Docker image runs node:22. Every page renders real HTML on the first byte so AI crawlers (ChatGPT, Claude, Perplexity) can read the service catalog without executing JavaScript.
The website owns the MCP server at /mcp, the buyer guides under /skills/,
/llms-full.txt, and the current and legacy skill discovery indexes under
/.well-known/. Guides are bundled Markdown sources in src/skills/, rendered
with this instance's public GATEWAY_URL, SITE_URL, and network. The copied
agent prompt points at the website's /skills/setup.md.
MCP tools execute gateway REST calls using GATEWAY_INTERNAL_URL when set,
otherwise GATEWAY_URL. The server checks gateway chain metadata before tool
execution, preserves payer signatures and gateway-bound resource URLs, and
never retries transaction requests or falls back to another origin after a
failed request. An uncertain paid submission must be reconciled. Only the
client address selected by MCP_TRUST_PROXY is forwarded on private networking.
Set it to 1 behind Railway ingress and keep the gateway's TRUST_PROXY=1 for
that private hop; direct local servers use 0. Public gateway
routing retains the gateway's own ingress client address.
The website owns no transaction state, payer keys, chain signer, or database.
Its MCP tool names, schemas, and result envelopes retain the established buyer
contract. /.well-known/mcp.json publishes the website
transport with capabilities and supported CLI versions from the gateway.
The setup guide reads the live CLI pin rather than duplicating it in prose.
Mainnet without a configured gateway returns 503 from guides and MCP instead
of publishing sandbox purchase instructions.
The gateway's former documentation URLs redirect here, and its former MCP URL
uses HTTP 307 to preserve client POST requests. npm run test:runtime checks
compiled Astro routes, local MCP guide retrieval, the tool contract, and the
legacy redirect through an offline REST fixture. It then starts the built
server the way the container does and requires HTTP 200 on / and
/health/ready.json.
npm install
npm run devThe same build serves either network. An instance is told its network at
request time through the variables in .env.example; with none
of them set it is the Testnet sandbox site, reading the public Daski Gateway
at https://sandbox-gateway.daski.io.
| Variable | Purpose | Default |
|---|---|---|
DASKI_NETWORK |
testnet or mainnet |
testnet |
GATEWAY_URL |
public gateway origin for the server and the browser | the sandbox gateway on testnet, unset on mainnet |
GATEWAY_INTERNAL_URL |
server-only origin over Railway private networking | unset |
MCP_TRUST_PROXY |
trusted ingress hops for MCP client admission; use 1 behind Railway | 0 |
NETWORK_NOTICE |
strip under the header: testnet, mainnet-soon, none |
derived from the network and gateway |
SITE_URL |
this instance's canonical origin | the network's public host |
TESTNET_SITE_URL, MAINNET_SITE_URL |
header switch targets | https://sandbox.daski.io, https://daski.io |
EXPLORER_URL |
block explorer origin | Basescan for the configured chain |
SITE_ROBOTS |
index or noindex |
index |
Chain facts (chain ID, name, explorer) live in src/lib/chains.ts, keyed by
network. A mainnet instance without GATEWAY_URL renders its empty states and
the launching-soon strip instead of failing. A gateway that reports another
chain is refused, and an unknown DASKI_NETWORK or a malformed URL fails
readiness rather than quietly serving another network.
/public/v3/services is the sole catalog source. Service detail routes use
the gateway-issued canonical serviceId, and category filters are derived
from returned services so new provider categories remain discoverable. The
website does not apply provider, product, jurisdiction, or skill allowlists.
Historical chain activity remains a separate gateway-fed view.
GATEWAY_URL=http://localhost:3000 npm run devServer rendering can reach the gateway over a different origin than the
browser does. GATEWAY_INTERNAL_URL is server-only; when set, SSR fetches use
it and the browser keeps using GATEWAY_URL. If the internal origin fails,
SSR falls back to the public origin and retries the internal one a minute
later.
GATEWAY_INTERNAL_URL=http://gateway.railway.internal:8080Gateway payloads are cached in the server process for 30 seconds and refreshed in the background, so pages render from the last good payload instead of waiting on the gateway.
npm run build # astro build (server output via @astrojs/node standalone)
npm run test:runtime # verify the built agent interface and the server start against an offline gateway
npm run preview # serve the production build locally
npm start # equivalent to: node ./dist/server/entry.mjsRailway picks up Dockerfile + railway.json. The container runs
node ./dist/server/entry.mjs — the standalone Node server emitted by the
Astro Node adapter, which renders every route on demand.
Set GATEWAY_INTERNAL_URL=http://gateway.railway.internal:8080 on the
website service so server rendering reaches the gateway over Railway private
networking instead of the public Cloudflare hop. The gateway must listen on
IPv6 for private networking to work; SSR falls back to the public origin if
the internal one fails.
Two instances run the same image: the sandbox service with no variables set
at sandbox.daski.io, and the production service with DASKI_NETWORK=mainnet
at daski.io. Launching mainnet is setting GATEWAY_URL and
GATEWAY_INTERNAL_URL on the production service; nothing else changes.
Releases (develop→sandbox merges, versioning) are coordinated from
daski-io/deploy-testnet: its
coordinator merges the release pull request into sandbox and tags the merge
commit, and Railway deploys the sandbox site from sandbox. main is the
production branch and moves only by fast-forward, performed by the production
coordinator, to a release commit that already ran on the sandbox. Emergency
fixes branch from main as hotfix/<id>, and their pushes run CI. Never
merge to sandbox or main or tag by hand.
/llms.txt and /robots.txt are rendered from the instance configuration,
and contract addresses come from the gateway's chain metadata, so nothing in
public/ is hand-maintained per network.
Push to develop only when docs/release-readiness.md
is satisfied: releases promote the exact commit CI proved, and anything a change
needs at deploy time goes in that commit's Release-* trailers.