Skip to main content
TinyCloud write hooks let you react to changes in kv, sql, and duckdb without polling. The same tc.hooks surface supports two delivery modes:
  • SSE subscriptions for live, in-process consumers
  • Durable webhooks for external systems that need POST delivery and retries
Both modes use the same scope model:
FieldMeaning
spaceThe TinyCloud space to observe
serviceOne of kv, sql, or duckdb
pathPrefixOptional path filter within that service
abilitiesOptional ability filter. Supported values are tinycloud.kv/put, tinycloud.kv/del, tinycloud.sql/write, and tinycloud.duckdb/write
A pathPrefix matches the exact path or anything below it. For example, documents matches documents and documents/notes.

Supported Services

Write hooks are available for:
  • kv
  • sql
  • duckdb
For sql and duckdb, hook paths use the database/table form:
<db_name>/<table_name>
For kv, the path is the key that was written or deleted.

Live SSE Subscriptions

Use tc.hooks.subscribe() when your app can stay connected to TinyCloud and process events directly. The SDK multiplexes logical subscriptions onto one physical stream per host and invoker.
import { TinyCloudNode } from "@tinycloud/node-sdk";

const tc = new TinyCloudNode({
privateKey: process.env.WALLET_PRIVATE_KEY!,
host: "https://node.tinycloud.xyz",
});

await tc.signIn();

const stream = tc.hooks.subscribe([
{
space: tc.spaceId!,
service: "sql",
pathPrefix: "main.db",
abilities: ["tinycloud.sql/write"],
},
]);

for await (const event of stream) {
console.log(event.type, event.service, event.path, event.actor);
}

subscribe() returns an async iterable of raw type: "write" events. Each event is emitted after the write succeeds and is filtered by the scope you requested. The stream is live-only: there is no replay cursor, backfill, or missed-event recovery on reconnect.

Durable Webhooks

Use tc.hooks.register() when the receiver lives outside the current process and needs HTTP POST delivery.
const created = await tc.hooks.register({
  space: tc.spaceId!,
  service: "kv",
  pathPrefix: "documents",
  abilities: ["tinycloud.kv/put", "tinycloud.kv/del"],
  callbackUrl: "https://example.com/tinycloud/hooks",
  secret: process.env.TINYCLOUD_WEBHOOK_SECRET!,
});

if (created.ok) {
console.log(created.data.id);
}

Webhook registration records include the subscription scope, callback URL, active flag, and creation time. The secret is stored encrypted on the server; keep the plaintext secret only in your own deployment or secret manager. The secret field is required. TinyCloud signs each delivery with X-TinyCloud-Signature: sha256=<hex hmac> over the raw request body.

Event Shape

Write events follow the same raw payload shape across SSE and webhooks:
{
  type: "write",
  id: string,
  space: string,
  service: "kv" | "sql" | "duckdb",
  ability: string,
  path?: string,
  actor: string,
  epoch: string,
  eventIndex: number,
  timestamp: string
}
path is the key for KV events and <db>/<table> for SQL and DuckDB events. Webhook deliveries use the same event payload as the HTTP body and are signed with X-TinyCloud-Signature: sha256=<hex hmac>.

Delivery Semantics

SSE is live-only. There is no replay cursor or backfill, and if a client falls behind or reconnects after a gap, missed events are not recovered. Webhooks are durable and at-least-once. Duplicate deliveries are possible, so receivers must be idempotent. For KV webhooks, durable enqueue happens from the KV commit path. For SQL and DuckDB webhooks, durable enqueue happens after the write succeeds, so the delivery record is not in the same atomic transaction as the data write. That weaker atomicity is intentional and matches the shipped implementation.

Self-Hosting Notes

If you self-host TinyCloud, hook-related limits live under the [hooks] config section:
[hooks]
max_ticket_ttl_seconds = 300
max_scopes_per_ticket = 32
max_active_sse_streams = 100
sse_broadcast_capacity = 1024
max_webhook_subscriptions_per_space = 5
webhook_timeout_seconds = 10
webhook_max_attempts = 5
The same values can be overridden with nested TINYCLOUD_ environment variables such as TINYCLOUD_HOOKS__MAX_TICKET_TTL_SECONDS=300 and TINYCLOUD_STORAGE__DATABASE="postgres://user:pass@localhost:5432/tinycloud".
If you use filesystem-based staging or any temp-file/rename path during local runs, keep TMPDIR on the same filesystem as the node data volume to avoid cross-device EXDEV rename failures.

When To Use Which

  • Use SSE when your consumer is already inside your app or worker process.
  • Use webhooks when you need external delivery, retry handling, or decoupled processing.
  • Use kv for key-level change notifications, and sql or duckdb when you need table-level write hooks.