@activityplug/server
v1.0.2
Published
GraphQL and HTTP server surfaces for ActivityPlug.
Maintainers
Readme
@activityplug/server
@activityplug/server exposes ActivityPlug through a versioned HTTP API,
GraphQL, WebSocket streams, and an optional browser backend-for-frontend (BFF).
Applications that own their configuration and storage construct it through the
createActivityPlugServer() API. The activityplug-server command lives in
@activityplug/cli.
Node.js 26 or newer is required. The package uses ECMAScript modules.
Command-line server
@activityplug/cli provides the activityplug-server command. It bundles the
Mastodon, Misskey, Pleroma, Hollo, and HackersPub adapters and uses
process-local stores:
npx @activityplug/cli \
--allow-origin https://social.exampleSee the @activityplug/cli README for its options. Applications that need
durable stores should construct the server through this package instead.
Installation
Install the server and its peer dependencies:
pnpm add @activityplug/server @activityplug/core @hono/node-server @logtape/logtape graphql honoInstall each adapter that your program imports directly. For example:
pnpm add @activityplug/mastodonThe package root contains the supported public API:
import * as activityplug from "@activityplug/server";The examples below use named imports so their required configuration is visible.
Programmatic server
Applications should construct the adapters, origin policy, stores, and listener explicitly:
import { createMastodonAdapter } from "@activityplug/mastodon";
import {
createActivityPlugServer,
createNodePinnedWebSocketFactory,
createOriginPolicy,
nodeLookupAddresses,
} from "@activityplug/server";
const originPolicy = createOriginPolicy(["https://social.example"]);
const webSocket = createNodePinnedWebSocketFactory({
originPolicy,
lookup: nodeLookupAddresses,
});
const server = createActivityPlugServer({
adapters: [createMastodonAdapter({ webSocket })],
originPolicy,
tokenImport: { enabled: false },
});
await server.ready;
try {
server.start({ hostname: "127.0.0.1", port: 4000 });
await new Promise<void>((resolve) => {
process.once("SIGINT", () => resolve());
process.once("SIGTERM", () => resolve());
});
} finally {
await server.close();
}Without an explicit originPolicy, the constructed server rejects every remote
request. allowPrivateNetworks changes address filtering only; it does not
allow an origin that the policy rejects.
ready resolves after the owned security-state lifecycle starts. Requests also
wait for it. close() is idempotent and closes listeners created by this
server. Injected store clients, database pools, and other dependencies remain
caller-owned and must be closed after the server.
Choose an API surface
- Use
server.servicefor calls inside the same Node.js process. - Use
/api/v1for the versioned HTTP API and/api/v1/openapi.jsonfor its OpenAPI document. - Use
/graphqlfor GraphQL queries and mutations. - Use
/api/v1/streams/*for the HTTP API's WebSocket streams. - Configure
browserand use/v1/browser/*when an application needs an HttpOnly cookie BFF instead of exposing ActivityPlug session IDs to browser JavaScript.
Public HTTP and GraphQL clients send ActivityPlug session IDs in
Authorization: Bearer. Browser routes reject that header and bind
authentication to the __Host-activityplug cookie.
Browser configuration
Browser mode requires a public HTTPS origin, a 32-byte or longer signing key, and browser and stream-ticket stores:
import {
createActivityPlugServer,
InMemoryBrowserSessionStore,
InMemoryStreamTicketStore,
} from "@activityplug/server";
const server = createActivityPlugServer({
adapters,
originPolicy,
browser: {
publicOrigin: "https://app.example",
cookieSigningKey,
browserSessions: new InMemoryBrowserSessionStore(),
streamTickets: new InMemoryStreamTicketStore(),
},
});The server supplies in-memory OAuth state, authentication challenge, and authentication-start limiter stores when they are omitted. These defaults, the stores shown above, and the default authentication session store lose state on restart. Production deployments should inject durable implementations for every lifecycle store they use.
Anonymous browser sessions are stateless by default. Set
anonymousSessionMode: "stored" only when server-side allocation is required;
stored mode applies global, per-client, and creation-rate admission limits. A
direct deployment can use the verified transport peer as the client identity.
A deployment behind a proxy should provide a resolver that trusts forwarding
headers only from known proxy addresses.
Routes
The principal entry points are:
| Route | Purpose |
| --------------------------------- | --------------------------------------- |
| GET /health | Process and dependency readiness |
| GET /api/v1 | HTTP API version and discovery links |
| GET /api/v1/openapi.json | HTTP API contract |
| POST /graphql | GraphQL API |
| GET /api/v1/streams | Public stream protocol metadata |
| GET /api/v1/streams/* | Public WebSocket streams |
| GET /v1/browser/session | Browser session and CSRF bootstrap |
| /v1/browser/auth/* | Browser authentication flows |
| /v1/browser/api/* | Cookie-authenticated browser operations |
| POST /v1/browser/stream-tickets | Single-use browser stream ticket |
| GET /v1/browser/stream | Ticket-authenticated browser stream |
The complete HTTP operation list is published by the running server's OpenAPI document. Browser routes intentionally expose a smaller product-facing surface.
Storage and security choices
The default stores are suitable for tests, examples, and single-process
development. Durable deployments must keep related records in compatible
stores. In particular, a durable authentication session store requires a
matching oauthClientSecrets store.
Configure the following according to the surfaces you enable:
sessionsandoauthClientSecretsfor authentication sessions and OAuth client secrets;browserSessions,oauthStates,streamTickets,authStartLimiter, andauthChallengesfor browser mode;readinessto include durable dependencies inGET /health;requestLimitsfor transport bodies, remote structured responses, and WebSocket buffering;graphqlLimitsfor GraphQL document shape and resolver concurrency;createBudgetScopefor per-operation remote request, byte, node, concurrency, and deadline budgets;remoteCredentialGrantswhen a credential may be sent to an origin other than its issuer;clientIpwhen rate limits run behind a trusted reverse proxy.
See server usage, browser integration, session storage, security model, and errors and troubleshooting.
License
Licensed under Apache-2.0 OR MIT. See LICENSE-APACHE and LICENSE-MIT.
