YouBothAgent▾
You — Business rules and flows you own. Read these yourself.
Both — Know the idea; your agent follows the details.
Agent — Conventions and references your agent follows. Look up as needed.
General▾
Interface▾
Observability▾
Performance▾
Native▾
Development▾

Edge Computing

In Akan, edge computing means one Akan server calls another with the same generated fetch your app already uses. Add one option, { origin }, and the call goes to the other server.
Cloud server
Decides what should happen and sends commands to the edge.
Edge server
Does the work close to the device or user, and reports status back.
fetch + { origin }
Connects both sides with typed signal calls; only { origin } differs from a local call.

Call Another Server

Every generated fetch call takes an options object as its last argument. Put { origin } there and the call goes to that server instead of your own.
  • The origin ends in the API prefix. fetch appends the endpoint path to it as-is, as in https://edge-01.example.com/api.
  • Read the prefix, never write it. It is configurable, so build the origin with getApiPrefix() from akanjs/base instead of an /api literal.
  • The caller needs the endpoint too. A fetch holds only the endpoints its own app and libs declare, so put the ones the edge serves in a lib both apps use, or run one app on both sides.
A health check that pings one edge server:
apps/myapp/lib/_edge/edge.service.ts
  • fetch.ping() is built in. Every Akan server answers it with "ping", so you declare no endpoint for it.
  • An unreachable server throws. A refused connection or a timeout arrives as an Err, so the check catches it and answers false.
  • Give a probe a short timeout. Without one, a dead edge holds the call for the 30-second default.
Options for a remote call
originstringquery · mutation · pubsub
The server that receives the call: scheme, host and API prefix.
timeoutnumber | falsedefault 30000query · mutation
Milliseconds before the caller gives up; overrides the endpoint's timeout, and false waits.
tokenstringquery · mutation
Sent as Authorization: Bearer <token>, so the remote guards judge that account.
onResync() => voidpubsub
Runs after the room is subscribed again following a dropped connection.

Send Commands

When the cloud wants the edge to do something, call an ordinary query or mutation with the same { origin }. Arguments and return values stay typed. Build the origin once and pass it to each call:
apps/myapp/lib/_edge/edge.service.ts
  • Long work needs a longer deadline. A call gives up after 30 seconds unless the endpoint declares { timeout } or the caller passes one.
  • Declare it on the edge endpoint. For firmware updates, provisioning and other slow jobs, every caller then gets the same budget.

Errors Come Back

An Err the remote endpoint threw arrives here as that same Err: same key, same data, and instanceof Err. Let it propagate, and your own caller gets it too, so the browser toasts the sentence the remote server chose.
One error crossing two servers:
One error, two servers
What the caller catches
When
↳ Thrown on the caller
The remote endpoint threw an Err
The same Err, with its key, data and status code
The connection was refused or the host did not resolve
base.error.serverUnreachable (503)
No answer before the timeout
base.error.gatewayTimeout (408)
A proxy in front answered 502, 503 or 504 with its own page
base.error.serverUnavailable · base.error.gatewayTimeout

Listen To Status

When the edge keeps sending status, subscribe to its pubsub endpoint with the same { origin }. The call returns an unsubscribe function; keep it and call it when you are done:
apps/myapp/lib/_edge/edge.service.ts
  • One socket per edge server. The first subscription with an origin opens a websocket to that server, and later ones share it.
  • Events sent during a disconnect are lost. Pass onResync to reload the current state once the room is back.
  • The edge's pubsub declares its own guards. A slice's guard map does not reach a pubsub, so a room without guards is open to any socket.

Wrap A Remote Node

When you talk to the same edge server many times, wrap it in a small class that remembers its origin and its unsubscribe functions:
apps/myapp/srvkit/RemoteEdge.ts
  • The origin lives in one place. Every method reuses #origin, so no call can forget the { origin } option.
  • close() releases every subscription at once. Call it when the edge goes offline or the worker stops.
  • A plain class, not adapt(). There is one per edge server, not one per process, so create it with new RemoteEdge(host) where you need it.

Very Fast Data

Keep commands and status on Akan fetch. Bytes such as telemetry or video frames can ride pubsub(Binary); add another transport only for streams even that cannot carry.
fetch.startJob(...)
Commands. A query or mutation, with typed arguments and a typed result.
fetch.subscribeJobStatus(...)
Status. A pubsub room the edge publishes to.
pubsub(Binary)
Telemetry, video frames. Binary websocket frames; a slow subscriber gets the newest one.
A separate transport
Huge streams. Add one only when pubsub(Binary) is not enough.
  • Bytes skip JSON. When the whole return is Binary, each payload goes out as a websocket binary frame and arrives as a Uint8Array.
  • A slow subscriber gets the newest frame. Declare { backpressure: "queue" } on the pubsub when every frame must arrive, such as deltas against a base.
  • Never send bytes as Any. A Buffer inside Any turns into a JSON number array about 3.6 times larger.

Run The Edge Site

An edge site is usually one container that keeps its data in SQLite files. The cloud can run the same app as a cluster, from the same image.
Declare both database modes in akan.config.ts:
apps/myapp/akan.config.ts
Each deployment of the image then says where it runs and where its data lives:
SettingEdge siteCloud cluster
AKAN_PUBLIC_OPERATION_MODEedgecloud, the image default
AKAN_DATABASE_MODEsinglecluster
DataSQLite files on a mounted volume that AKAN_SQLITE_DIR namesPOSTGRES_URL and REDIS_URI
InstancesOne containerSeveral servers
  • The operation mode and the database mode are independent. edge or cloud says where the server runs; single or cluster says where its data lives.
  • Only internals follow the operation mode. An internal declared as cron("0 4 * * *", { operationMode: ["cloud"] }) never runs on the edge, while every endpoint is served on both sides.
  • Every deployment names its mode. With two modes declared, AKAN_DATABASE_MODE is required; only a developer machine falls back to the first.

Tips

  • Start with a normal signal. If it works locally, it can usually be called remotely by changing only { origin }.
  • Keep edge hosts in the database. Build each origin from getApiPrefix(), so a prefix change reaches every one of them.
  • Always clean up subscriptions. Otherwise a long-running worker leaks connections.
Related pages

Released under the MIT License

Connect your AI to these docs

MCPhttps://akanjs.com/mcp
Copyright © 2026 Akan.js All rights reserved.System managed bybassman