clave docs

Code in a site

A site is files by default. A site can also run code: an Astro action, an API route, or a page rendered on demand. Pages stay files, and a page view runs no code.

The plan must allow code. Until it does, a publish with code is refused.

Turn it on

Add the adapter to astro.config.mjs. It is already installed.

import cloudflare from '@astrojs/cloudflare';

export default defineConfig({
  adapter: cloudflare(),
  // ...
});

Do not use Astro.session. It needs a binding the site does not get.

What the code gets

In env Is
DB the site's store, when the site has migrations
CLAVE_API, CLAVE_SITE_KEY the runtime API; a draft gets a key of its own
CLAVE_ENV live or draft
your own names the site's secrets; the draft has values of its own

A request from the code goes out only to hosts on the site's egress list (egress in clave.site.json), and to the runtime API. Each call has limits: 50 ms of CPU time and 50 requests.

clave.site.json

A file named clave.site.json at the root of the repository holds the site's settings. Three of its keys say what the code asks of the platform: events, schedules and private. egress is the list of hosts the code may call.

{
  "events": ["payment.settled", "payment.refunded"],
  "schedules": [{ "name": "reminders", "cron": "0 9 * * *" }],
  "private": ["/staff/", { "path": "/admin/", "roles": ["owner"], "labels": ["admin"] }]
}

A fault in it stops the publish, and the message says which part.

Events

The platform sends POST /clave/events to the live site for each event it asked for. The body is:

{ "id": "evt_...", "kind": "payment.settled", "created_at": "...", "data": { "payment_id": "pay_...", "reference": "order_123", "amount_minor": 2500, "currency": "eur", "late": false } }

The events today:

Kind data
payment.settled payment_id, reference, currency, amount_minor, late
payment.refunded payment_id, reference, currency, amount_minor, refunded_minor

reference is the site's own, and is empty when the site gave none. late is true when the money arrived after the hold ran out: the site's code decides what that means. refunded_minor is the total given back so far, not the amount of this one refund. It equals amount_minor when all of it went back.

Answer with a 2xx only when the event is handled. A 2xx is final: an event answered with one is never sent again, even when the code ignored it. Any other answer, a redirect included, or no answer in 30 seconds, is sent again. The wait starts at 30 seconds and doubles to at most one hour, for 24 hours. An event can arrive twice, so keep the ids you handled in the store.

Write the page at src/pages/clave/events.ts. Only the platform can call it: a request to /clave/ without the platform's signature gets a 404. The platform checks the signature and takes the header off, so the page never sees it and has no need to check it.

Schedules

The platform sends POST /clave/schedules/{name} when the cron expression is due. The draft keeps its own timers, from the clave.site.json it was built with, and they go to the draft's address. Five fields, in UTC. The shortest interval is 15 minutes. A cron in wrangler.jsonc does nothing.

Private paths

A path in private is for the site's staff, who sign in with Clave. A draft guards its own private paths the same way, on its own address. The site's code then gets a Clave-Identity on every request. See Staff.

A path is a string, or { "path": ..., "roles": [...], "labels": [...] }. A string opens for anyone with a role on the site. With roles or labels, a person needs one of those roles (owner, collaborator, staff) or one of those labels, from the staff list. When two paths match, such as /admin/ and /admin/money/, the person must pass both. At most 5 paths, and 5 labels for each.

Logs

Logs answers with what the code logged and threw in the last hour.

Restore

Restoring an old version brings back code and not data. The store stays as it is, so code must not assume the schema it had yesterday.