Architecture overview
GuildGate centralizes security decisions and write operations for a Discord dashboard server. It does not render a UI, own application data, or require a specific HTTP framework.
Kernel
createGuildGate() validates configuration at startup and creates these surfaces:
gate.action()for guarded operation definitionsgate.execute()for request executiongate.sessionsfor session managementgate.cachefor tag-based cachinggate.ownerfor maintenance and block policiesgate.revokeSession()andgate.revokeUserSessions()for revocation
The kernel applies origin, session, CSRF, policy, rate limit, idempotency, lock, transaction, audit, and realtime decisions under one request ID.
Action layer
An action combines these parts in one definition:
- Parse input
- Resolve the resource
- Check identity and CSRF
- Authorize access
- Apply rate limits and idempotency
- Check optimistic revision and acquire a distributed lock
- Perform the domain write in a transaction
- Process cache, realtime, and audit results after commit
In 1.1.0, an action returns at its configured deadline even when the underlying operation ignores AbortSignal. Reservation and lease ownership for a late idempotent result are retained only for a configured bounded period.
Store layer
GuildGateStores consists of ten small contracts:
| Member | Responsibility |
|---|---|
sessions | Session lifecycle and per-user cap |
oauthStates | Single-use OAuth state consumption |
credentials | Encrypted OAuth credentials |
rateLimits | Atomic counters and windows |
cache | TTL, stale windows, and tag invalidation |
idempotency | Reservation ownership and replay results |
locks | Leases, renewal, and fencing tokens |
audit | Immutable events and cursor paging |
outbox | Event delivery with claim leases |
policies | Maintenance, blocks, and policy version |
composeStores() combines different backend bundles. For example, persistent records can live in PostgreSQL while short-lived counters and leases use Redis.
Transaction layer
TransactionAdapter lets a domain write and required audit or outbox records share one commit boundary. The PostgreSQL adapter uses savepoints for nested transactions.
A callback failure after a successful COMMIT does not make the domain write look rolled back. The committed result remains final and the issue is exposed through TransactionPostCommitError or postCommitIssues in action metadata; the write is not repeated.
Realtime layer
createRealtimeHub() separates connection authorization and delivery limits from the transport. WebSocket and Socket.IO messages pass through the same payload, rate, subscription, activity, and backpressure controls. SSE output can use the same session and channel authorization model.
Sequence and replay support lets clients resume after a disconnect. In multi-worker deployments, events can be claimed and delivered through the outbox.
HTTP and Discord adapters
The package publishes action handlers for Fastify, Express, and Hono. The discord.js adapter maps guild and permission data into the narrow interface expected by the kernel.
The kernel does not own route structure, database schema, or the Discord client's lifecycle. Those decisions stay in the application.
See the operation lifecycle for the detailed request order.