
Security News
Deno Goes the Way of the Dinosaur
Cloudflare is acquiring Deno’s team, which will provide another year of bug fixes and security updates before ending support for the runtime.
The Tempo API is Tempo's next-generation data API. It is designed for clients that need a high-level surface for exploring the Tempo chain.
The Tempo API is a composable Hono app. You can run your own instance (Self-hosted) or call the hosted API directly (Hosted).
Call the hosted Tempo API directly at https://api.tempo.xyz:
curl https://api.tempo.xyz/v1/tokens
const response = await fetch('https://api.tempo.xyz/v1/tokens/0x…')
const token = await response.json()
Explore the API interactively at the root /.
Install the package:
pnpm i tapimo
Stateful features (webhook subscriptions, verified tokens) live in Postgres. Provision a database and apply migrations with the CLI:
DATABASE_URL=postgresql://… tapimo admin migrate
The API is composed from route groups (apps) imported from tapimo/apps.
Create a base app with App.create and mount the apps you want:
import { data, routes } from 'tapimo/apps'
import { App, Db } from 'tapimo/server'
const app = App.create({
db: Db.postgres({ connectionString: process.env.DATABASE_URL }),
})
.route('/', data())
.route('/', routes())
| App | Purpose | Resources (non-exhaustive) |
|---|---|---|
data() | Chain and indexed data reads | Blocks, transactions, tokens, webhooks |
routes() | Asset routing | Quotes, chains, providers, deposits, transfers |
relay() | Wallet relay & fee sponsorship | Relay, sponsor |
management() | Tenancy and access management | Orgs, projects, members, API keys |
The composed app exposes a Web fetch method, and App.listener adapts it
to a Node.js request listener, so you can serve it from any runtime or
framework:
createServer(App.listener(app)) // Node.js
Bun.serve({ fetch: app.fetch }) // Bun
Deno.serve({ fetch: app.fetch }) // Deno
server.all('*', (c) => app.fetch(c.request)) // Elysia
server.use(App.listener(app)) // Express
server.use((c) => app.fetch(c.req.raw)) // Hono
export const GET = app.fetch // Next.js
export const POST = app.fetch // Next.js
Use the typed client for endpoint autocomplete and API-key/header configuration:
import { Client } from 'tapimo'
const client = Client.create({
// API key sugar for the canonical `tempo-api-key` header.
apiKey: 'tempo_api_key',
// Override the API URL. Defaults to `Client.defaultUrl`.
url: 'https://api.tempo.xyz',
// Or pass custom headers directly.
headers: { authorization: 'Bearer tempo_api_key' },
})
Requests are end-to-end type-safe across params, statuses, response bodies, and errors. For example, GET /v1/tokens/:token:
const response = await client.v1.tokens[':token'].$get({
param: { token: '0x20c0000000000000000000000000000000000000' },
})
// Narrow responses by status
if (response.status !== 200) {
response.status
// ^? (property) status: 400 | 401 | 402 | 403 | 404 | 429 | 502
if (response.status === 404) {
const json = await response.json()
// ^? const json: { error: { code: "token_not_found"; message: string; ... }; requestId: string }
json.error.code
// ^? (property) code: "token_not_found"
} else if (response.status === 402) {
const challenge = await response.text()
// ^? const challenge: string
// Handle the payment challenge from `WWW-Authenticate`.
} else {
const json = await response.json()
// ^? const json: { error: { code: "query_invalid" | "api_key_invalid" | "upstream_error" | ...; ... }; requestId: string }
// Handle validation, auth, rate-limit, or upstream errors.
}
throw new Error('Request failed')
}
response.status
// ^? (property) status: 200
const token = await response.json()
// ^? const token: { address: Hex.Hex; currency: string; decimals: number; name: string; ... }
token.symbol
// ^? (property) symbol: string
Clone the Horcrux backend as a sibling checkout (the checkout must include the
horcrux dev local-signing backend), then prepare this checkout:
git clone https://github.com/tempoxyz/horcrux.git ../horcrux
pnpm setup:env
Set these values in the ignored repo-root .env:
HORCRUX_URL=http://127.0.0.1:3040
HORCRUX_ACCESS_CLIENT_ID=
HORCRUX_ACCESS_CLIENT_SECRET=
HORCRUX_ROUTES_TRANSFER_SUBSIDY_WALLETS=0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266
ROUTES_SUBSIDY_MAX_AMOUNT=0.10
Start the opt-in stack with docker compose --profile subsidies up -d --build.
Set TEMPO_API_HORCRUX_SOURCE if the sibling checkout is elsewhere. Compose mounts
the public seeds/horcrux-wallets.json fixture read-only and starts horcrux dev
with HORCRUX_DEV_WALLETS=/etc/horcrux/wallets.json. To customize the preinitialized
wallets, copy the manifest, preserve its schema, use stable UUIDs, and set
TEMPO_API_HORCRUX_WALLETS to that host path. Horcrux atomically initializes the
fixed development team/grant and refuses metadata, key, ID, address, or grant
conflicts. It preserves wallets, signed history, and replay state on restart; do not
delete its database volume as a routine reinitialization step.
The fixture contains EVM, Tron, and Solana wallets and uses only publicly compromised developer keys. Never use these keys for real credentials or send real funds to any derived address. The EVM wallet is public Anvil account 0. API route subsidies still use only that EVM address through the normal Horcrux viem SDK; local Horcrux does not add API-managed Tron or Solana subsidies. Direct Horcrux HTTP clients identify all wallet families by the manifest UUID, while the viem integration can look up its EVM wallet by address. The dev backend exposes the normal EVM/Tempo, Solana, Tron, and hash signing APIs without policies or broadcasting.
Horcrux shares the API container's network namespace and listens only on loopback;
unauthenticated SDK access is accepted only by an API with
ENVIRONMENT=development, a loopback HTTP(S) URL, and both Cloudflare credentials
absent. After changing .env or the manifest,
run docker compose restart api horcrux. This profile starts a signer only: it does
not start bridges, Tron, Solana, Base/Ethereum forks, or other chains. Point the
relevant RPC URLs at disposable nodes yourself. Signing locally does not change
where the API broadcasts. Fund the EVM address with disposable destination tokens
and fee funds on the tested chain only when exercising settlement.
ROUTES_SUBSIDY_MAX_AMOUNT is a per-subsidy cap, not a total spending budget.
Developers not exercising Routes subsidies can leave
HORCRUX_ROUTES_TRANSFER_SUBSIDY_WALLETS empty and run the API without Horcrux
access. Fee sponsorship keeps its existing setup; there is no private-key fallback
for Routes subsidies.
Set HORCRUX_ROUTES_TRANSFER_SUBSIDY_WALLETS to a comma-separated list of EVM
wallet addresses registered in Horcrux. The first address funds new commitments;
retain retiring addresses until their existing commitments settle. Address-based
horcruxAccount construction is synchronous and requires no discovery request.
These wallets need the destination token and transaction fee funds on each
supported destination chain. Fee sponsorship, MPP settlement, and rewards signing
remain separate.
The first EVM wallet also signs inbound Tempo deposit subsidies through Horcrux sign-hash. Fund it with pathUSD and its selected fee token on Tempo. Transfer reservations do not count deposit top-ups, so monitor the shared Tempo balance for both obligations. Relay fixed-rate deposit addresses use Relay's 1:1 subsidy.
The SDK is pinned to @zygis-tempo/horcrux-viem@0.1.0 on npm, aliased as
@horcrux/viem. The SDK owns the Horcrux signing protocol; the API still owns
inventory checks, nonce reservations, transaction persistence, and broadcast.
For production, apps/api/wrangler.background.jsonc owns HORCRUX_URL and
HORCRUX_ROUTES_TRANSFER_SUBSIDY_WALLETS. CI and pnpm -C apps/api deploy run
apps/api/scripts/prepare:production.ts to copy only those values into the HTTP
Worker's generated wrangler.deploy.jsonc. The shared wrangler.jsonc has no
production Horcrux defaults, so local development and previews cannot inherit them.
The HTTP deployment keeps queue producers but leaves consumers, cron, and the
reward signer binding on api-background.
Keep HORCRUX_ACCESS_CLIENT_ID and HORCRUX_ACCESS_CLIENT_SECRET as Cloudflare
secrets on both production Workers before rollout. They are not stored in Wrangler
configuration or read from GitHub Actions. If the URL or wallet list was previously
stored as a Cloudflare secret, remove that secret binding before deploying the
same name as a plain-text Wrangler variable.
For PR and main-tracking previews, configure these once in the API repository's
GitHub Actions settings. CI uploads them to each preview Worker under the same
names without the _DEV suffix; no manual Worker configuration is needed.
| GitHub Actions setting | Name | Value |
|---|---|---|
| Secret or variable | HORCRUX_URL_DEV | Cloudflare-accessible runtime API base URL of the Horcrux instance containing the dev wallet (not its admin URL) |
| Secret or variable | HORCRUX_ROUTES_TRANSFER_SUBSIDY_WALLETS_DEV | Comma-separated EVM wallet addresses, active first, not UUIDs |
| Secret or variable | HORCRUX_ROUTES_TRANSFER_SUBSIDY_TRON_WALLETS_DEV | Comma-separated Tron address and Horcrux UUID pairs, active first |
| Secret | HORCRUX_ACCESS_CLIENT_ID_DEV | Dev Cloudflare Access client ID |
| Secret | HORCRUX_ACCESS_CLIENT_SECRET_DEV | Dev Cloudflare Access client secret |
Secrets take precedence when both a secret and variable have the same name. The credential pair must be complete or absent. Setting a wallet list requires the URL and both credentials. Removing both wallet settings disables transfer subsidies on the next preview deployment; drain pending commitments first. Absent credentials leave existing Worker credentials unchanged. Use only dev wallets and an identity authorized for those wallets. CI's disposable local signer needs none of these settings.
Grant the workload access to each subsidy wallet and permit its destination-chain
ERC-20 transfers. ROUTES_SUBSIDY_MAX_AMOUNT remains the operational USD limit.
An empty wallet list disables transfer subsidies; enabled subsidies with incomplete
Horcrux credentials fail closed. There is no private-key fallback.
For Tempo USDT0 to Tron USDT, set HORCRUX_ROUTES_TRANSFER_SUBSIDY_TRON_WALLETS
to comma-separated TronAddress:HorcruxWalletUUID pairs, active first. Fund those
wallets with Tron USDT and TRX. Set ROUTES_TRON_SUBSIDY_FEE_LIMIT_SUN to the
maximum native fee reserved for each TRC-20 transfer. The API persists Tron
transaction bytes before Horcrux signing and reuses the same signature and
transaction on retries. Keep retiring wallets configured until their pending
transfers settle. The production wallet mapping belongs in
apps/api/wrangler.background.jsonc; the deploy script copies it into the HTTP
Worker when present. An empty mapping leaves Tron transfer subsidies disabled.
This replaces ROUTES_TRANSFER_SUBSIDY_ACCOUNT and
ROUTES_TRANSFER_SUBSIDY_PRIVATE_KEY. For migration, register the existing wallets
in Horcrux or drain their unsigned commitments before switching addresses. After
cutover, remove the unused private-key secrets from Cloudflare and GitHub;
deployment workflows no longer upload them. Persisted signed transactions still
replay through the existing nonce and settlement records. Token top-up billing
is unchanged; destination gas remains paid by Tempo.
Run the configuration tests with disposable local infrastructure:
pnpm test:local apps/api/src/RoutesTransferSubsidies.test.ts --run
Paste into your agent:
Read https://github.com/tempoxyz/api/blob/main/README.md, then spin up the
Tempo API container and provide me links to the services.
pnpm install # Install dependencies
pnpm dev # Start the local stack (Docker Compose)
pnpm test # Run tests against live resources
pnpm test:local # Run tests against local resources
pnpm dev spins up:
Reward campaign administration is available locally, but scheduled settlement is production-only; the development stack intentionally has no production signer or reviewed factory binding.
| Service | Purpose | URL |
|---|---|---|
api | Tempo API | http://api.tempo.local |
console | Developer Console | http://console.tempo.local |
postgres | Database | localhost:5432 |
tempo | Tempo Node | http://rpc.tempo.local:8545 |
tidx | Indexer | http://tidx.tempo.local:8080 |
stripe | Stripe CLI (Webhook Forwarder) | - |
FAQs
Unknown package
The npm package tapimo receives a total of 12,677 weekly downloads. As such, tapimo popularity was classified as popular.
We found that tapimo demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 7 open source maintainers collaborating on the project.

Security News
Cloudflare is acquiring Deno’s team, which will provide another year of bug fixes and security updates before ending support for the runtime.

Security News
Anthropic’s free OSS Scanner uses Mythos and other Claude models to find open source vulnerabilities and send maintainers reports with proposed fixes.

Security News
On Front Lines’ Early Adopters: CISO podcast, Andrew Becherer shares lessons from Datadog, weighs the long-term costs of AI-built tools, and explains how founders can actually gain a CISO’s trust.