Skip to content

What we use, and why ​

Every outside service, library and piece of infrastructure, with one line on what it does for Salvo. Status uses the home page states, with "testnet" and "test key" written out. Hosting rows say Running, and the probe row says Measured. Env vars are listed by name only.

Which of these cost money and do nothing yet

Accounts that can cost money: Railway, Stripe (per payment, and only on a live key), Resend, ParcelsApp, Lit, and any proxy or paid RPC. ParcelsApp, Lit and a proxy do no work in production today, because what each serves is not running yet.

Money coming in ​

ServiceWhat it is forStatusEnv
StripeTakes card payments for Cinemark tickets through Stripe Checkout, and refunds them. For gift cards at the register: saves the shopper's card once, holds the price when the barcode opens, then charges or cancels the hold. Gift card resale needs Stripe's approval before a live key; see Where the money goes🟢 Cinemark on a test key. 🔵 Gift card holds built, never run against Stripe. A live key is refusedSTRIPE_SECRET_KEY, STRIPE_WEBHOOK_SECRET
x402, with the x402.org facilitatorLets an AI agent pay for an order in USDC over HTTP 402. The facilitator checks the buyer's signed transfer and submits it on chain, so our receiving address needs no funding🔵 Built, never run. Not configured in productionX402_PAY_TO, X402_NETWORK, X402_FACILITATOR_URL
MPP on Tempo (mppx)A second machine-payment protocol. Agents pay in a stablecoin on the Tempo chain🟢 Testnet only: two settlements on moderatoTEMPO_*, MPP_SECRET_KEY
Crypto refunds (viem)Sends the token back to a buyer from our wallet, after checking it holds enough of the token. Gas and fees are not checked. Never for a Salvo Pay order: those refuse before any key is read🔵 BuiltX402_PRIVATE_KEY, TEMPO_PRIVATE_KEY
Salvo Pay accounts (Tempo passkey accounts, viem/tempo)The shopper's and seller's own account on Tempo's test network. A passkey (Face ID) is the root key; a scoped access key on the phone signs without a prompt, only into the escrow, up to a weekly limit the chain enforces. Testnet faucet for test dollars. No sponsor is configured, so the phone pays its own fee🔵 Built, tested offline. Off unless ESCROW_RAIL=testnetESCROW_RAIL, TEMPO_SPONSOR_URL (optional)

See Payment rails.

Money at rest ​

ServiceWhat it is forStatusEnv
MorphoLends idle escrow into a vault on Base to earn yield for the reserve. Payouts never wait on it. Only scripts can reach it🔵 Built, no transaction sentMORPHO_*
Multi-chain escrow (Tempo mainnet and Moderato)Would hold a buyer's money in a contract until release or refund. The escrow pool, payout policy and chain registry have tests, but no contract they can read is deployed, so every escrow read fails closed to unfunded. The gift card escrow (SalvoEscrowV1, next row) is separate🔵 Built, no contract deployedCHAINS_ENABLED, CHAIN_*
Gift card escrow on Tempo, second version (SalvoEscrowV2)Holds a dollars shopper's payment at the till: the value used of each card, and a deposit at face on what is left on the last. A card sells in parts; what is left stays listed for the next shopper first. The tap is one batch; Lit's release runs beside the second host's agreement, and the purchase is booked once both agree. No owner or admin. The keeper follows it beside V1🔵 Built. Deployed on Tempo's test network on 2026-10-13, at 0xa30d1631bf644fa608dacc02b6d2aebbc1cd200b, pinned and bound; its live smoke passed. Open to shoppers (ESCROW_V2_LIVE): new sales go here, and V1 takes nothing new. The till's dollars rail on it is tested offline only. Not reviewedESCROW_RAIL, ESCROW_KEEPER_KEY, ESCROW_SELLER_ALLOWLIST
Gift card escrow on Tempo (SalvoEscrowV1)Frozen for new sales since V2 opened: what it sold still releases, pays out and refunds. Held a Salvo Pay shopper's payment. Paying and opening the card are one transaction; the seller is paid 24 hours later by anyone's call; only the seller can refund. No owner or admin. Read from two pinned public hosts that must agree. Our keeper, in the 15-minute job, records sales and, with its key set, makes the payout call🔵 Built, with live testnet scripts (escrow:*). Deployed on Tempo's test network on 2026-10-10, at 0x75c9aabe8254559f865e14d6e5fa70c738869bef. Its creation check and live smoke passed. Not reviewed: on the testnet a listing is taken as the operator's own cardESCROW_RAIL, ESCROW_KEEPER_KEY, ESCROW_SELLER_ALLOWLIST

See Morpho.

Keeping card numbers safe ​

ServiceWhat it is forStatusEnv
Local envelope vaultEncrypts each card number and PIN under its own key, wrapped by a master key. This is what runs today. Every listing is recorded as the operator's, including any card a stranger types into the public sell screen (an open decision)🔵 BuiltVAULT_MASTER_KEY
Lit Protocol (Chipotle, Lit V3), keys derived in a TEEEach card is encrypted to a key that only one exact piece of code holds, inside Lit's enclave. Meant for other people's cards once the unlock rule reads the escrow contract. Today's rule is our own signature, so it is custodial and refused for sellers🔵 Rebuilt. Live smoke passed on Lit on 2026-10-10None in production. The smoke reads the usage key from --key-file; setup uses the account key from the shell
Lit escrow template (salvo-vault-escrow/1)The unlock rule that reads the escrow. It signs a ticket before any money moves, and releases a card only for a final purchase, encrypted to the buyer's phone key. Our routes relay it and see ciphertext only🔵 Built, frozen, and pinned to the testnet escrow. Its signer and sealing key are pinned from a live bind on Lit, and the live smoke passed on 2026-10-10. Not counted non-custodial: on the testnet a listing is taken as the operator's own cardLIT_VAULT_USAGE_KEY at runtime, with the rail on

See Custody.

Stores ​

ServiceWhat it is forStatusEnv
Best Buy and Nike balance pagesReads a card's balance from the store's public, no-login page, so a sale can settle on fact🔵 Built, never read a real card. Nike's result field is a guessnone
Best Buy and Nike checkoutsFills a card into the store's own checkout, for online redemption🔵 Built. Best Buy's steps come from saved pages; Nike's checkout was never captured. Live use switched offBESTBUY_ALLOW_LIVE_REDEEM, NIKE_ALLOW_LIVE_REDEEM, BESTBUY_CLIENT_*
Cinemark driverBuys a ticket for the buyer from the operator's own Cinemark account, paid with Movie Club credits🔵 Built, never run for real. Both testnet orders used a stand-in insteadFULFILLER, CINEMARK_*
Toast, Square and Clover probesRead-only checks of whether a venue takes gift cards. Toast and Clover answer per venue; Square's probe can only say unknown🟢 Measured: Toast across 127 venuesnone
BarcodesDraws the store's barcode on the buyer's phone. Code 128, ITF and Code 39 are written in-house. Target's PDF417 is drawn on our server with bwip-js and sent with the page, so the phone loads nothing extra🔵 Built, never scanned at a registernone
Apple Wallet passAdds a pass to Apple Wallet. It holds a short-lived signed reference, not the card; the store's barcode only appears on the reveal page🔵 Built, unsigned: no certificateWALLET_*
Cards drawn on the phone (Salvo Pay)A bought card's barcode, drawn on the shopper's phone with bwip-js's browser build, from the card the phone decrypted: at the till in scan order, and on Account for cards bought on the first escrow. Our server never sees it. Passes built on the phone, for Apple Wallet, are a later phase🔵 On-screen code built, never scanned. Phone-built passes not builtnone

See Three kinds of gift card.

Delivery ​

ServiceWhat it is forStatusEnv
ParcelsAppConfirms a parcel was delivered, which releases the first stage of a cash advance payout. It replaced EasyPost, which kept tracking behind a manual review we could not clear. Nothing in production uses it yet: no live call has been made, and the cash advance has no storage🔵 Built, never called livePARCELSAPP_API_KEY, DELIVERY_CONFIRMER

See Cash advance.

Messages ​

ServiceWhat it is forStatusEnv
ResendEmails buyers when an order is fulfilled or refunded. With no key, the email is logged instead🔵 BuiltRESEND_API_KEY, EMAIL_FROM
ntfy, self-hosted on RailwayPushes every order state change to the operator's phone. Self-hosted because pushes from the Railway worker to the public ntfy.sh timed out, for a reason never foundRunning. Checked end to end on a smoke orderNTFY_SERVER, NTFY_TOPIC

Hosting and data ​

ServiceWhat it is forStatusEnv
RailwayRuns four services. The web app, a worker that holds the Cinemark session, and a reconcile job every 15 minutes share one Docker image; ntfy runs from its own. A push to main redeploys web, worker and cron, and web skips a push that touches none of its watched pathsRunningset by Railway
PostgreSQL, through PrismaStores orders, their event log, gift card listings, and webhook de-duplication. Card numbers are stored only as ciphertextRunningDATABASE_URL, TEST_DATABASE_URL

See Operations.

Building and testing ​

ToolWhat it is for
Next.js and ReactThe app
VitePressBuilds these docs into the app, on the same domain
PlaywrightDrives Chromium for the Cinemark driver, the balance pages, the checkouts and the UI audit. It ships with the app, not only with the tests
VitestThe test suite, run against a separate schema on the Railway database
An HTTP proxy: a residential service, or tinyproxy on a VPS as the runbook sets upOnly for the session-portability test, which has never been fired. PORTABILITY_PROXY_*
viem, zod, tsxChain calls; checking env vars the first time they are used; running scripts

What we do not use ​

  • No paymaster. There is no paymaster, bundler or smart account. Every wallet is a plain private key. The two nearest things: the x402 facilitator submits the buyer's USDC transfer, and Tempo fee sponsorship, whose testnet relay URL is stored in the code but never used. Some comments say the Tempo key co-signs sponsored fees; the payment call passes no fee payer, so buyers pay their own Tempo fee from their stablecoin. Salvo Pay is the one place a wallet is not a plain key: its accounts are passkeys with a scoped access key, Tempo's own account type, and they also pay their own fee.
  • No analytics, error tracking or telemetry.
  • No SMS.
  • No sign-in provider. Buyers and sellers have no accounts. The operator page is protected by a shared secret.