graft. docs

Functions & access

Typed functions own operational data. Mutations deny anonymous callers by default.

Operational data (submissions, orders, comments) is written only through functions defined in config or under graft/:

export const submitContact = defineFunction({
  name: "submitContact",
  kind: "mutation",
  public: true, // greppable opt-out of the anonymous deny default
  rateLimit: { limit: 5, windowSeconds: 60 },
  input: submissions.fields,
  handler: async (ctx) => {
    const record = await insertRecord(ctx, submissions, ctx.input);
    return { id: record.id, receivedAt: record.createdAt.toISOString() };
  },
});

Serve at POST /api/fn/<name> (JSON object body) via a framework mount or graft serve. Success returns { data }. Failure returns a GraftError with fix. Every response carries x-graft-correlation-id.

Secure by default

  • Mutations reject anonymous callers unless public: true
  • access: requireScopes("submissions:read") replaces the default with a scope rule
  • rateLimit counts against the audit log — no separate counter service
  • Every invocation writes an audit_log row (actor, correlation id, git SHA)

Context

Handlers receive FunctionContext: { input, db, actor, branch, request, correlationId }. Use core helpers (insertRecord, listRecords, updateRecord, deleteRecord, searchRecords) so authority and Zod validation stay enforced.

Destructive operations

Mark destructive: true for irreversible work. First call files an approval and returns 403 with the id. A human runs graft approve <id>. The caller retries with x-graft-approval (HTTP) or approval (MCP). Approvals are one-shot and input-bound. See Audit & approvals.

Identity

@usegraft/auth verifies bearers. Graft does not mint identities. See Auth.