Architecture & encryption
Two layers
Section titled “Two layers”The installable package (oase-mcp) is split so that “how to talk
to Oase” is cleanly separated from the tool surface:
| Layer | Where | What it is |
|---|---|---|
| Passive REST client | src/client/ |
Everything that knows how to interact with the Oase API over HTTP: Promise login/auth, token refresh and shared config, and the full REST client — invite-link joining, key fetching, AES-256-GCM encryption/decryption, and sending/reading messages and posts, reactions, and media. Request/response only: no socket, no realtime push. No MCP dependency, no agent behavior. |
| MCP server | src/mcp/ |
The tool surface exposed to MCP clients. All tools are on-demand wrappers over the passive layer — the server itself never acts unprompted. |
The passive layer has a clean exported surface (importable as
oase-mcp), so other consumers — a bot, a bridge, your own
integration — can build on the API client without pulling in the MCP
layer. dist/index.js is the MCP server entry point, so client
registrations keep working across versions.
Identity
Section titled “Identity”The agent is a persistent
Promise user — the same identity
provider the Oase app uses. A one-time browser login exchanges a
single-use Promise id_token for Oase’s own long-lived refresh token;
from then on Promise is never contacted again. No Promise credentials are
stored — only the resulting Oase refresh token, in
~/.oase-mcp/config.json (mode 0600). Access tokens are short-lived,
kept in memory, and refreshed automatically; refreshes are serialized
across concurrent server processes via a lock file, because the backend
rotates the refresh token on every refresh and revokes the session if it
ever sees a stale one (anti-replay).
Encryption
Section titled “Encryption”The server talks to the production Oase backend exactly like the app does: sign in → join via invite link → fetch the oase key from the KMS → AES-256-GCM encrypt → post the message.
Oase encrypts message content with a per-oase symmetric AES-256-GCM key held in escrow by the backend: every participant of an oase can fetch that oase’s raw key from the KMS by presenting a backend-signed proof of membership. The MCP server does the same, then produces the exact cipher-bundle shape the app expects — so its messages decrypt and render normally for everyone in the oase. There are no device keypairs or enrollment ceremonies involved.
No message is ever sent in plaintext — the send endpoint requires a
cipher bundle, and decryption of received messages happens client-side in
the server. Media attachments are encrypted the same way: modern uploads
travel as an encrypted container using the same per-oase key (with the
original filename and mime type encrypted alongside), and read_media
decrypts them locally.