This page is for Signalite operators. It explains what must be configured before a selected partner can run a new-user flow smoothly.
Never paste private keys into docs, tickets, chat, or client-side env vars. Secrets belong only in Railway or a local operator terminal.
1. Database schema
Run after every deploy that changes db/schema.sql:
This creates or updates:
- partner clients
- partner actions
- partner webhooks
- webhook delivery logs
- faucet rate-limit logs
2. Partner API key
Create a server-side partner key:
PARTNER_NAME="Get Arc" npm run partners:create
The command prints the raw key once and stores only a hash in Postgres.
Partner apps must store this key server-side only:
SIGNALITE_PARTNER_API_KEY=shlp_...
For the full production check/create flow, prefer the bootstrap command:
WRITE_PARTNER_SETUP=1 \
DATABASE_URL="$DATABASE_URL" \
PARTNER_NAME="Get Arc" \
npm run partners:bootstrap
This creates or updates the partner client, prints the raw key once, and then probes the live API with that key.
3. Webhook registration
The partner backend registers its callback URL with the partner API key:
await shlp.upsertPartnerWebhook({
url: "https://partner.example.com/webhooks/signalite",
events: ["partner_action.created", "partner_action.submitted"],
enabled: true,
rotateSecret: true,
});
Store the returned signingSecret. It is returned only on creation or rotation.
Operator bootstrap equivalent:
WRITE_PARTNER_SETUP=1 \
DATABASE_URL="$DATABASE_URL" \
PARTNER_NAME="Get Arc" \
PARTNER_API_KEY="shlp_..." \
PARTNER_WEBHOOK_URL="https://partner.example.com/webhooks/signalite" \
PARTNER_WEBHOOK_ROTATE_SECRET=1 \
npm run partners:bootstrap
If PARTNER_API_KEY is omitted, the command generates a new key and uses it for the webhook registration.
4. Railway cron jobs
The web/API service should have these scheduled calls:
| Job | Path | Cadence |
|---|
| Vault event indexer | POST /api/cron/index-vault | every 1 minute |
| Snapshot writer | POST /api/cron/snapshot | every 5 minutes |
| Webhook retry | POST /api/cron/webhooks | every 1 minute |
Every cron request must include:
Authorization: Bearer <CRON_SECRET>
The same CRON_SECRET must be configured in Railway for the web/API service.
Verify cron auth without triggering any wallet action:
API_BASE=https://portal.signalite.ai \
CRON_SECRET="$CRON_SECRET" \
npm run partners:bootstrap
For a brand-new mobile user with no HyperEVM HYPE, authenticated Partner Actions must sponsor gas before returning wallet signature steps.
Required Railway env:
GAS_SPONSOR_ENABLED=true
GAS_SPONSOR_PRIVATE_KEY=<secret, Railway only>
GAS_SPONSOR_MIN_BALANCE_HYPE=0.001
GAS_SPONSOR_AMOUNT_HYPE=0.01
GAS_SPONSOR_DAILY_HYPE_CAP=0.1
GAS_SPONSOR_COOLDOWN_SECONDS=3600
FAUCET_ENABLED=false
The gas sponsor EOA must hold enough HyperEVM HYPE. The legacy public faucet
toggle should stay disabled for production partner launch; sponsorship is
reached through authenticated Partner Actions.
Verify:
curl "https://portal.signalite.ai/api/v1/gas/status?address=0x..."
The response must include:
{
"faucetEnabled": false
}
Strict partner-mobile check:
API_BASE=https://portal.signalite.ai \
REQUIRE_GAS_TOPUP=1 \
npm run partners:bootstrap
6. Readiness gates
Bootstrap report:
API_BASE=https://portal.signalite.ai npm run partners:bootstrap
Report mode:
API_BASE=https://portal.signalite.ai npm run readiness:partner
Strict mode:
PARTNER_READY_REQUIRED=1 API_BASE=https://portal.signalite.ai npm run readiness:partner
Strict mode should fail until:
- keeper heartbeat is fresh
- keeper release SHA is reported
- indexer and snapshots are fresh
- OpenAPI schema is complete
- server-side gas sponsorship is configured and funded
- deposits are ready, unless explicitly excluded for a non-deposit demo
7. Not complete until observed
Before a partner demo is considered production-ready, observe:
- first-time Partner Action gas sponsorship
- deposit with approval
- deposit without approval after allowance exists
- instant redeem inside buffer
- queued redeem above buffer
- cancel pending redeem
- claim claimable redeem
- webhook delivery and retry path
- activity and redeem rows updated by the indexer