Skip to content

XRPC proxy

Control which unrecognized XRPC methods HappyView forwards, and where they go. Lexicons registered on the instance are always served, whatever this setting says.

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 routing looks up where a lexicon is defined. For com.atproto.repo.* that’s the account publishing those lexicons, and the request carries no credentials, so a write to the caller’s repo works only 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
400A new NSID pattern is not a valid NSID, or a wildcard pattern has fewer than two segments before .*, an empty segment, or a character other than letters, digits and hyphens. Patterns already stored are not checked again
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"
}