Highlights
- WebSocket Gateways —
ArkosGateway, built on Socket.io, with the same conventions as the rest of the framework: reuses your existing authentication, plugs intoArkosPolicyfor per-event authorization, reuses your Zod/class-validator setup, and shares the global error handler. - Deduplication and freshness checks built into the event pipeline, with a pluggable store (in-memory by default, bring your own for Redis/multi-instance).
- Client packages —
@arkosjs/websockets-client(framework-agnostic) and@arkosjs/react-websockets(hooks + provider) for consuming Gateways from the frontend. - Dynamic-mode permission overrides — permissions can now be granted or denied to individual users on top of their role, via a new
UserPermissionmodel. - Static file serving, upload pipeline improvements, and
orderBypassthrough to Prisma.
WebSocket Gateways
// src/gateway.ts
import { ArkosGateway } from "arkos/websockets";
const gateway = ArkosGateway({ name: "/", authentication: true });
gateway.on({ event: "send_message" }, (socket, data) => {
socket.to(data.room).emit("receive_message", data);
});
export default gateway;// src/server.ts
import { Server } from "socket.io";
import http from "http";
import app from "@/src/app";
import gateway from "@/src/gateway";
const server = http.createServer(app);
const io = new Server(server);
gateway.register(io);
app.listen(server);gateway.register(io) can only be called once per io instance — compose additional namespaces with .use() (either connection middleware, or a nested ArkosGateway that inherits the parent's authentication and rateLimit, though not its .pipe()s).
Authentication and authorization
authentication: truereuses whatever auth mode (static/dynamic) is already configured inarkos.config.ts— there's no separate WebSocket auth setup. On success,socket.currentUseris populated and the socket auto-joins an internal per-user room; on failure the connection is rejected before any handler runs.- If a Gateway sets
authentication: truewithout any authentication mode configured for the app, Arkos throws at startup rather than silently accepting unauthenticated sockets. - Per-event authorization plugs into
ArkosPolicy:
gateway.on(
{ event: "delete_message", authorization: chatPolicy.DeleteMessage },
handler
);Registering an event with authorization on a Gateway that has authentication: false throws immediately, at registration time — not on the first request.
Validation, dedup, and freshness
validationon an event reuses your existing Zod/class-validator resolver — no separate WebSocket validation system.- Deduplication is on by default for every event (
{ enabled: true, ttl: 3600 }seconds), keyed ondata._meta.mid. A duplicatemidskips your handler and acks{ success: true, duplicate: true }rather than erroring, since the first delivery already succeeded. Disable per event (dedup: false) or per Gateway. maxAgerejects messages older than a given window, based on_meta.timestamp. A cheap future-timestamp guard (1-second tolerance) runs on every message with a timestamp, regardless of whethermaxAgeis set.- Both resolve event → Gateway → parent Gateway, with the most specific config winning.
_metais injected automatically on every outgoing emit (socket.emit,emitWithAck,socket.to().emit(),socket.broadcast.emit()) and is what the client SDKs rely on for this — if you're emitting from a rawsocket.io-clientconnection instead of the SDK, you're responsible for sending_metayourself.
Targeting
socket.to() and socket.broadcast return an enhanced broadcast operator with .except({ user }) (exclude every active connection of one or more users) and .users() (unique user IDs in the target room). socket.user(userId) targets every active connection of a specific user and adds .activeRooms(). socket.retry(times, baseDelay?, multiplier?) wraps emitWithAck with exponential backoff.
Stores
Rate limiting and deduplication share one ArkosGatewayStore interface (increment, clear, has, set, setIfNotExists). The default is in-memory and single-instance only. Implement the interface against Redis for distributed deployments, or chain stores with MultiTierArkosGatewayStore for a fast local tier in front of a shared one.
Client packages
These are pre-1.0 and still maturing.
| Package | Status |
|---|---|
@arkosjs/websockets-client (Vanilla JS) |
Stable core API |
@arkosjs/react-websockets |
Reference implementation, tested |
| Vue / Svelte / Solid / Angular bindings | Under development — starting-point implementations exist but haven't been validated in a real app; tracked at #260, #258, #259, #261 |
The vanilla client wraps a socket.io-client Manager, injects _meta automatically on emit, and exposes .emit(event, data, { ack, timeout, retries }) returning { success, data, error }. The React binding adds <ArkosSocketProvider>, useGateway(namespace) (with reactive .status/.user), and chat.useEmit(event) (with .loading/.error/.reset()).
Dynamic-mode permission overrides
AuthPermission moves from one row per role to a shared row referenced by many roles (roles: AuthRole[], unique on [resource, action]), and a new UserPermission model lets you grant or deny a specific permission to an individual user (effect: Allow | Deny), independent of their role. See the migration guide if you're upgrading an existing Dynamic-mode project — it involves a staged Prisma migration, not just a config change.
Static files, uploads, query
- Built-in static file serving via
express.static, with corrected default path resolution and CORS removed from the default middleware stack (add it explicitly if you relied on it). - Nested array upload paths, upload config inheritance/drilling, a customizable body-attachment function, and
[] → [0]field naming in generated OpenAPI schemas. req.query.orderBycan now be forwarded directly to Prisma'sorderByinput.
Also in this release
- Scalar is now available as an alternative to Swagger for
/api/docs. - CLI generator templates renamed (
middlewares-template→interceptors-template), naming only.
Upgrading
pnpm install arkos@latestResources
- Full documentation
- WebSockets setup guide
- Dynamic permission overrides migration guide
- GitHub repository
What's Changed
- feat(websockets): move emit methods directly to Arkos socket to eliminate circular dependencies by @Uanela in #263
- Feat/websockets client by @Uanela in #285
- Promote
canary-1.7→canary→main(v1.7.0) by @Uanela in #286 - Revert "Promote
canary-1.7→canary→main(v1.7.0)" by @Uanela in #287 - Canary 1.7 by @Uanela in #288
- Promote
canary→main(v1.7.0 GA) by @Uanela in #289
Full Changelog: v1.6.7-beta...v1.7.0-rc