Skip to content

XRPC proxy

Control which unrecognized XRPC methods HappyView forwards, and where they go. Locally registered lexicons are always served regardless of this setting.

All endpoints require the settings:manage permission.

const TOKEN = "hv_..."; // your API key
const headers = { Authorization: `Bearer ${TOKEN}` };
GET /admin/settings/xrpc-proxy
interface XrpcProxyConfig {
mode: string;
nsids: string[];
routing: "authority" | "serviceproxy";
}
const response = await fetch("http://127.0.0.1:3000/admin/settings/xrpc-proxy", {
headers,
});
const data: XrpcProxyConfig = await response.json();

Response: 200 OK

{
"mode": "allowlist",
"nsids": ["com.example.feed.*", "games.gamesgamesgamesgames.*"],
"routing": "authority"
}

Returns {"mode": "open", "nsids": [], "routing": "authority"} when no config has been saved.

PUT /admin/settings/xrpc-proxy
const response = await fetch("http://127.0.0.1:3000/admin/settings/xrpc-proxy", {
method: "PUT",
headers: {
...headers,
"Content-Type": "application/json",
},
body: JSON.stringify({
mode: "allowlist",
nsids: ["com.example.feed.*"],
}),
});

Response: 204 No Content

FieldTypeRequiredDescription
modestringyesWhether a method may be proxied
nsidsstring[]noPatterns for allowlist and blocklist
routingstringnoWhere a proxied method goes. Left out, the stored routing is unchanged

Changes take effect immediately, with no restart.

ModeBehavior
disabledBlock all proxy requests. Return 403 for every unrecognized NSID.
openProxy everything (default).
allowlistProxy only NSIDs matching a pattern in nsids. Return 403 for the rest.
blocklistProxy everything except NSIDs matching a pattern in nsids.

When mode is disabled or open, any nsids in the request body are ignored and stored as [].

RoutingBehavior
authorityForward to the authority the NSID’s _lexicon DNS record names, with no credentials (default).
serviceproxyForward to the caller’s own PDS, authenticated as the caller, per service proxying. An atproto-proxy header is passed through, and the PDS relays the request onwards. An anonymous query is 401 Unauthorized, because the destination depends on who is asking.

authority answers where a schema is defined, not who serves a request. For com.atproto.repo.* that’s the account publishing those lexicons, so an authenticated write only works under serviceproxy.

Patterns are dotted NSID identifiers. Trailing wildcards are supported:

  • com.example.feed.getHot: exact match
  • com.example.feed.*: matches any NSID starting with com.example.feed.
  • games.gamesgamesgamesgames.*: matches the entire namespace

Mid-segment wildcards (e.g., com.*.feed) are not supported.

StatusCause
400An NSID pattern is empty, has fewer than two segments, contains invalid characters, or uses an unsupported wildcard
422mode is not one of disabled, open, allowlist, blocklist, or routing is not authority or serviceproxy

When the proxy denies a request, the client receives:

403 Forbidden
{
"error": "NSID not allowed by proxy policy"
}