Alarik can notify an external HTTP endpoint whenever objects in a bucket are created or removed - the self-hosted equivalent of Amazon S3 Event Notifications. Instead of targeting SNS/SQS/Lambda (which don't exist outside AWS), each rule points directly at a webhook URL.
Webhooks are configured per bucket by the bucket owner, under Webhooks in the console's object browser, or via the notifications API.
When an object operation matches an enabled rule, Alarik enqueues an event and delivers it asynchronously with an HTTP POST. Delivery is decoupled from the request that triggered it, so webhooks never slow down uploads or downloads, and events survive restarts and receiver outages (they are persisted until delivered).
Each rule can filter by event type and by object key prefix/suffix - see Set Bucket Webhooks for the full list of event types.
The request body is JSON in Amazon S3's event message structure (version 2.4), so existing S3-event tooling works unchanged:
{
"Records": [
{
"eventVersion": "2.4",
"eventSource": "alarik:s3",
"awsRegion": "us-east-1",
"eventTime": "2026-07-02T15:27:42.503Z",
"eventName": "ObjectCreated:Put",
"userIdentity": { "principalId": "alarik" },
"requestParameters": { "sourceIPAddress": "1.2.3.4" },
"responseElements": {
"x-amz-request-id": "…",
"x-amz-id-2": "…"
},
"s3": {
"s3SchemaVersion": "1.0",
"configurationId": "6ff08747-4ec0-48fd-a0b1-f5adda6d3161",
"bucket": {
"name": "my-bucket",
"ownerIdentity": { "principalId": "alarik" },
"arn": "arn:aws:s3:::my-bucket"
},
"object": {
"key": "uploads/red+flower.jpg",
"size": 1024,
"eTag": "d41d8cd98f00b204e9800998ecf8427e",
"versionId": "…",
"sequencer": "…"
}
}
}
]
}
Notes matching S3 semantics:
eventName omits the s3: prefix.key is URL-encoded (a space becomes +).versionId is only present for versioning-enabled buckets.sequencer is a hex value whose ordering reflects event order for a given key.If a rule has a secret, every delivery includes an X-Alarik-Signature-256 header containing the lowercase hex HMAC-SHA256 of the exact request body, keyed by the secret (the same scheme GitHub uses). Verify it before trusting a payload:
import hashlib, hmac
def is_valid(body: bytes, header: str, secret: str) -> bool:
expected = hmac.new(secret.encode(), body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, header)
Deliveries also carry User-Agent: Alarik-Webhook and Content-Type: application/json.
Delivery is at-least-once. A delivery is considered successful only on a 2xx response; anything else (including connection errors) is retried with exponential backoff (starting at ~1 minute, capped at 1 hour) for up to 8 attempts. Because a receiver may occasionally get a duplicate, handlers should be idempotent - the sequencer field helps de-duplicate per object key.
After 8 failed attempts a delivery is dead-lettered and retained for 7 days for inspection, then purged.
Use the console's Send test button, or the test endpoint, to deliver an s3:TestEvent message and confirm your receiver (and signature verification) works before relying on it.
Because Alarik itself makes the outbound request, webhook URLs are a potential SSRF vector. URLs that resolve to private, loopback, or link-local addresses (e.g. http://127.0.0.1:9000/hook or a 10.x/192.168.x LAN host) can therefore only be configured by administrators. Regular users may only point webhooks at public hosts. On a single-admin deployment this is transparent; on a shared multi-user instance it prevents one user from probing internal services.
GET /:bucket?notification returns the configuration as an S3 NotificationConfiguration document (rules appear as QueueConfiguration entries with an arn:alarik:webhook:::{id} ARN, since the S3 XML format has no URL field). Configuring notifications through the S3 PUT ?notification operation is not supported - it returns 501 NotImplemented, because the S3 format can only express SNS/SQS/Lambda ARNs. Use the console or the internal API instead.