Skip to main content
A published Site is a static or server-rendered frontend Arg hosts for you. Its frontend can use @arg/sdk in two different situations, with two different sources of authority.

Opened inside Arg

When a viewer opens the Site from Apps, or from a .app launcher, on web, desktop, iOS or Android, Arg wraps it with a bridge. Bundle the SDK into the Site like any dependency:
The Site sees the signed-in viewer, not its publisher, and only after that viewer enables Permissions → Workspace access for it. The grant starts off for every Site and every account, and works like an app preview’s:
  • Folder scope reads relative to the Site’s source folder in the workspace; Workspace scope, Read and write and Actions are separate choices.
  • It is remembered for that Site, origin and source folder, including published updates.
  • Revoking it, or changing account, replaces the guest so nothing survives in memory.
Credentials stay in Arg: the page never holds a token, and its normal fetch() calls and assets keep going to the Site’s own network. The bridge serves files, embedded SQLite, identity and members, chats, workspace discovery and Actions with their usual gates. Two limits: a shared-origin development URL does not get a bridge, only the Site’s canonical origin does; and Android needs a current system WebView.

Outside Arg, without a gateway

The same Site opened in an ordinary browser tab renders anonymously if it is public, or behind its normal viewing authorization if it is workspace-only. There, arg.ready rejects with bridge_unavailable and nothing attaches a viewer or publisher account. Handle it with your app’s signed-out state:

Hosted frontends outside Arg

A frontend that must work outside Arg - a Site with its own users, or any app you host - reaches the SDK through a gateway your app server mounts on the same origin. The browser half:
createHostedClient connects to that endpoint and nothing else: it does not log visitors in, does not exchange a Site’s viewing authorization for account access, and never attaches the publisher’s identity. Your server already owns an authenticated session for the visitor (or a service identity) and decides what that session may do.

The gateway

createGateway from @arg/sdk/gateway returns a (request: Request) => Promise<Response> handler for any standard Request/Response server:
authenticate runs on every request and returns the server-owned session: a workspace-bound transport, an explicit list of permitted operations, and an authorize check per call. Return null for an expired or revoked session and false from authorize to deny a call. Never pick the credential, workspace or role from anything the guest sent. The gateway itself checks one exact origin, the JSON content type and the protocol header, emits no CORS headers, rejects oversized bodies and hides internal error messages, following the non-simple request and origin defences for CSRF. It is a transport adapter: cookie verification, installation consent, audience binding, expiry, revocation and resource authorization belong in your authenticate and authorize.
Allowing actions.run is not a narrow file grant. Authorize the Action id and what it can do downstream: an Action that executes code or starts an agent must not receive a broader workspace or integration scope than the app was granted.

Servers with capability tokens

A Server (a long-running process on an HTTPS URL) receives its own Action capability token. On that server, use createCapabilityClient rather than an account credential; its frontend, if it needs the SDK, goes through a gateway exactly as above.