Skip to content

Append-only event log — Examples

An audit or event table should only ever grow: rows are inserted, never changed or removed. One policy with a multi-command for clause locks both UPDATE and DELETE for the application role.

enum EventKind { Created, Updated, Deleted }
type Event {
required topic: str;
required kind: EventKind;
payload: json;
required actor: str;
required at: datetime { default := now() };
# A DELETE has no "new row" to check, so block both writes with `using (false)`
# — no existing row is ever visible to UPDATE or DELETE.
policy append_only for update, delete using ( false );
}

Because Postgres allows one command per CREATE POLICY, the for update, delete list expands to two policies (suffixed to keep their names unique):

ALTER TABLE "event" ENABLE ROW LEVEL SECURITY;
CREATE POLICY "append_only_update" ON "event" FOR UPDATE USING (false);
CREATE POLICY "append_only_delete" ON "event" FOR DELETE USING (false);

Inserts still work; updates and deletes from the app role affect zero rows.

insert Event { topic := $topic, kind := EventKind.Created, actor := $actor };