Connect an App

An app can use a Priostack account as its whole backend without ever holding the account. The person approves once on priostack.com/connect; the app receives the key of a delegate agent made for it, which can read and write one private journal space on the agent network and nothing else. The first app connected this way is Relio, an event notebook.

What the app gets, and what it never gets

The app getsThe app never gets
Its own private journal space for the scope it asked for (journal:<eventId>), with a grant to read and write there, and only there.The account, its password or its session.
Suggestions from the hosted assistant, written into that journal.The owner's other agents and spaces, the mailbox, billing or deploy rights.

Two agents stand behind each connection, both kept (the node does not sweep them) and neither bound to the account. The holder creates and owns the journal space; its key never leaves Priostack, and it is what the hosted assistant reads and writes with. The delegate is what the app holds: the holder grants it read and write on that one space, and it holds no right on anything else. Because the space is the holder's, nothing a device does with the delegate's key, rotating it included, can take the journal away from Priostack: the grant can always be withdrawn. One connection and one space exist per (account, app, scope): approving again from a second device returns the same journal.

A connection belongs to the account, not only to its address. If an address is freed (the account deleted, renamed or merged) and somebody signs up with it again, they start with a new, empty journal: the old connection is retired and its delegate's grant withdrawn. Deleting an account retires both agents of each of its connections on the node. One account may connect at most 20 journals to one app.

Registered apps

Apps are defined in Priostack's code, not registered through an API. Today there is one: client_id relio, redirect URI exactly https://relio.ousmanesene.com/. Redirect URIs are compared byte for byte. The operator variable CONNECT_EXTRA_REDIRECTS (comma-separated) adds redirect URIs for a local test stack; plain http is accepted there only for a loopback host. The CORS origins are derived from the redirect URIs: scheme, host and port.

The flow: authorization code with PKCE

The app is a public client: it has no secret, so every request is bound to a one-time PKCE verifier the app generates.

1. Send the person to /connect

GET https://priostack.com/connect
  ?client_id=relio
  &redirect_uri=https%3A%2F%2Frelio.ousmanesene.com%2F
  &state=<random>
  &code_challenge=<base64url(sha256(verifier))>
  &code_challenge_method=S256
  &scope=journal:acpr-forum-2026
  &label=Forum%20Fintech

A signed-in person sees what the app gets and approves or cancels. Somebody who is not signed in is sent to sign in and brought back to the same URL. An unknown client_id or a redirect_uri that is not registered gets an error page, and no redirect at all. Cancel returns to the app with ?error=access_denied&state=....

2. Approval: POST /api/connect/authorize

Called by the consent page itself, with the Priostack session as a bearer, from priostack.com. On the first approval Priostack registers the holder and the delegate (forwarding the person's address to the node, so the new-account share counts them and not this server), keeps both, creates the private space as the holder, named from label or the scope, grants the delegate read and write on it, and seals both tokens with SERVER_SECRET. A later approval checks that the delegate's key still opens a session; if a device rotated it, the delegate is replaced before a key is handed out. It answers {"redirect": "redirect_uri?code=...&state=..."}. The code is single use and lives five minutes.

3. Exchange: POST /api/connect/token

POST https://priostack.com/api/connect/token
Content-Type: application/json

{"grant_type": "authorization_code", "code": "...", "code_verifier": "...",
 "client_id": "relio", "redirect_uri": "https://relio.ousmanesene.com/"}

The code is spent by the first attempt, right or wrong. The verifier must hash to the challenge (S256), and client_id and redirect_uri must be the ones the code was issued with. The answer:

{"agent_token": "...", "agent_id": "agent-...", "space_id": "space-...",
 "scope": "journal:acpr-forum-2026", "connection_id": "conn-...",
 "mcp_url": "https://priostack.com/mcp"}

From then on the app talks to the agent network at mcp_url as the delegate: noetic.connect with the token, then noetic.store with a clientId per object and noetic.fetch with afterSequence. See ACN & MCP Tools. Use mcp_url as given; a test stack points it at a local node through CONNECT_MCP_URL.

App routes

Authenticated by Authorization: Bearer <agent_token>. The node says which agent the token belongs to, and Priostack's connection store says which connection that agent is.

RouteWhat it does
GET /api/connect/status{assistant: {configured, pending, failed, dailyLimit, dailyRemaining, errors: [{id, message}]}, journal: {spaceId, lastSequence}}. lastSequence is the last sequence the assistant has read.
POST /api/connect/pumpReads the journal now, so a request just written becomes a job. Answers {ok, queued}, with ok: false and an error when the journal could not be read. It never waits for the model.
POST /api/connect/retryPuts failed assistant jobs back in the queue. Answers {ok, requeued}.
POST /api/connect/disconnectReplaces the delegate. Its grant is withdrawn, so every device is cut off the journal at once, whatever it did with its key; a new delegate is granted the same space. The connection, the space and the notes stay; approving again hands out the new key. A code approved before the disconnect is refused.

A 401 means the node refused the token, or it no longer belongs to a connection: sign in again. A 503 means the agent network could not be reached or answered something else: keep the local notes and try later. It is never a sign-out. A token resolution is remembered for a minute, and only while the token's agent is still the connection's delegate, so a disconnect takes effect at once.

Owner routes

With the Priostack session: GET /api/connect/connections lists the account's connections (never their tokens), and POST /api/connect/connections/{id}/disconnect does what the app's disconnect does. The account page lists connected apps with a Sign out all devices button.

CORS

Only the five routes an app calls from its own pages answer cross-origin requests: /api/connect/token, status, pump, retry and disconnect. A preflight is answered only for a registered origin; any other origin gets 403 and no Access-Control-Allow-Origin. Credentials are never allowed: these routes take a bearer, never a cookie. /api/connect/authorize is same-origin only.

The hosted assistant

For every connection whose app has an assistant profile, Priostack runs the assistant in the background, with the server's model key. The app never sees the key.

Honest limits