Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/natureloved/DeadMan-Vault/llms.txt

Use this file to discover all available pages before exploring further.

The keeper cron is the automated heartbeat monitor at the core of Deadman Vault’s proof-of-life guarantee. On every scheduled invocation, it fetches the current Stacks block height, evaluates the lifecycle state of every active vault against the on-chain clock, advances any vaults that have crossed a threshold (warning → grace period → triggered), and settles any triggered vaults by executing the keeper’s on-chain withdrawal and per-beneficiary token transfers — all without any action from the vault owner or beneficiaries.

How the Keeper Endpoint Works

The keeper runs as a standard Next.js GET route handler at /api/keeper. Its execution time cap is set to 60 seconds via export const maxDuration = 60 — important to be aware of on Vercel’s Hobby plan, which has a lower function timeout ceiling. On each invocation the endpoint:
  1. Authenticates the caller — checks that the Authorization header matches Bearer <CRON_SECRET>. If CRON_SECRET is set and the header is missing or wrong, it returns 401 Unauthorized immediately.
  2. Fetches the current block height — calls the Hiro API (api.testnet.hiro.so/v2/info on testnet) to get the live Stacks block height.
  3. Runs runKeeper(currentBlock) — iterates all vaults in active, warning, grace_period, and triggered states and applies lifecycle rules in order.
  4. Returns a structured result — reports how many vaults were checked, what block the sweep ran at, and a list of actions taken.

Vercel Cron Configuration

The cron schedule is declared in vercel.json at the repository root. Vercel reads this file automatically during deployment — no dashboard configuration is needed:
{
  "crons": [
    {
      "path": "/api/keeper",
      "schedule": "0 0 * * *"
    }
  ]
}
This schedule runs the keeper once per day at midnight UTC (0 0 * * *). For a tighter heartbeat SLA — for example, advancing vaults into grace period within minutes of their deadline — change the schedule to a more frequent interval such as */15 * * * * (every 15 minutes). Keep in mind that Vercel Hobby plan limits cron frequency; the Pro plan supports per-minute invocations.
Vercel automatically injects the Authorization: Bearer <CRON_SECRET> header on scheduled cron invocations when CRON_SECRET is set as an environment variable in the Vercel project. You do not need to configure this manually in vercel.json.

Vault Lifecycle Rules

On each sweep, runKeeper applies the following rules to every active vault, in order:
1

Check current block vs. deadline

The keeper compares currentBlock against each vault’s heartbeat_deadline_block. Vaults where the deadline has not yet been crossed are skipped without any state change.
2

Warning

When the deadline is within a configurable warning window, the vault status advances to warning and the owner receives a warning email via Resend. Notification deduplication prevents repeat emails for the same vault event.
3

Grace period

After the deadline passes, the vault enters grace_period. A 48-hour grace window gives the owner one final chance to check in. An urgent notification email is sent.
4

Trigger

When the grace period expires without a heartbeat, the vault status changes to triggered and triggered_at is recorded. Beneficiary notification emails are sent with claim information.
5

Settlement

For triggered vaults, the keeper decrypts the vault’s keeper private key (using KEEPER_ENCRYPTION_KEY + AES-256-GCM), executes a FlowVault withdraw on the vault’s behalf, and then sends each beneficiary their proportional USDCx share directly. If a beneficiary has a vesting schedule, vesting_release_at is checked — only beneficiaries whose cliff has matured receive their transfer in this sweep. Vested-but-unmatured beneficiaries are retried on subsequent sweeps via the partial index on beneficiaries(vesting_release_at) WHERE claimed = FALSE.
The keeper is fault-isolated at the vault level. If settlement fails for one vault — due to a transient network error, an underfunded keeper address, or an on-chain broadcast timeout — that error is caught and logged, and the sweep continues processing all remaining vaults. A single failed vault never aborts the entire cron run.

Keeper Response Format

A successful sweep returns a JSON object:
{
  "checked": 5,
  "currentBlock": 182340,
  "actions": [
    "warning:a1b2c3d4-e5f6-...",
    "triggered:f7e8d9ca-b1c2-..."
  ],
  "timestamp": "2025-01-15T00:00:04.123Z"
}
FieldDescription
checkedTotal number of vaults evaluated in this sweep
currentBlockStacks block height at the time of the sweep
actionsArray of "<action>:<vault-id>" strings for every state transition or settlement that occurred
timestampISO 8601 timestamp of the sweep completion

Authentication

The /api/keeper endpoint validates the Authorization header against CRON_SECRET:
const cronSecret = process.env.CRON_SECRET;
if (cronSecret) {
  const authHeader = request.headers.get("authorization");
  if (authHeader !== `Bearer ${cronSecret}`) {
    return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
  }
}
The outer if (cronSecret) check means that if CRON_SECRET is not set, the endpoint skips authentication entirely — any HTTP client can trigger a sweep. Always set CRON_SECRET in production.

Testing the Keeper Manually

You can trigger a keeper sweep on demand using curl. This is useful for verifying your setup immediately after deployment or for forcing settlement during development:
curl -X GET "https://your-deployment.vercel.app/api/keeper" \
  -H "Authorization: Bearer <your-cron-secret>"
Replace <your-cron-secret> with the value of your CRON_SECRET environment variable. You can also test locally against the dev server:
curl -X GET "http://localhost:3000/api/keeper" \
  -H "Authorization: Bearer <your-cron-secret>"
During local development, trigger the keeper manually after creating a test vault and manually setting its heartbeat_deadline_block to a past block height in the Supabase Table Editor. The next keeper run will immediately advance it through the triggered and settlement states, letting you verify the full email and settlement flow end-to-end.

Build docs developers (and LLMs) love