slink is a command line tool for calling APIs described with Lexicon.
slink's API-calling functions are automatically generated by slink itself, and slink can be used to generate Go client libraries and CLIs for arbitrary Lexicon descriptions. With an optional manifest, generated code can be limited to only what is needed to support specific functions and records.
slink stores no state in local files; all configuration (including auth tokens) is instead read from environment variables. Using these variables, slink can be configured to make direct calls to XRPC services and slink can also connect to remote services through a proxy such as IO which handles routing and authentication.
To install slink on any system with Go installed:
- clone the repo.
- copy the lexicons directory to the root of the repo.
- run
make all. This will generate XRPC command handlers and a CLI in thegendirectory. It then builds everything to createslink.
It's this easy to use slink to call XRPC APIs:
% SLINK_HOST=https://inkcap.us-east.host.bsky.network slink call com.atproto.server describe-server
{
"availableUserDomains": [
".inkcap.us-east.host.bsky.network"
],
"contact": {},
"did": "did:web:inkcap.us-east.host.bsky.network",
"inviteCodeRequired": true,
"links": {
"privacyPolicy": "https://bsky.social/about/support/privacy-policy",
"termsOfService": "https://bsky.social/about/support/tos"
}
}
You just need to specify the host to be called with the SLINK_HOST environment variable (be sure to include the https:// or http:// prefix).
export SLINK_HOST=$YOUR_PDS_URL
With that, you can use slink to make unauthenticated calls.
$ slink call com.atproto.sync list-repos
{
"cursor": "...",
"repos": [
{
"active": true,
"did": "did:plc:...",
"head": "...",
"rev": "..."
},
{
"active": true,
"did": "did:plc:...",
"head": "...",
"rev": "..."
}
]
}
Use the slink built-in help to see what is possible:
$ slink call
Call XRPC methods
Usage:
slink call [command]
Available Commands:
app.bsky.actor Call XRPC methods under app.bsky.actor
app.bsky.ageassurance Call XRPC methods under app.bsky.ageassurance
app.bsky.bookmark Call XRPC methods under app.bsky.bookmark
app.bsky.contact Call XRPC methods under app.bsky.contact
app.bsky.draft Call XRPC methods under app.bsky.draft
app.bsky.feed Call XRPC methods under app.bsky.feed
app.bsky.graph Call XRPC methods under app.bsky.graph
app.bsky.labeler Call XRPC methods under app.bsky.labeler
app.bsky.notification Call XRPC methods under app.bsky.notification
app.bsky.unspecced Call XRPC methods under app.bsky.unspecced
app.bsky.video Call XRPC methods under app.bsky.video
chat.bsky.actor Call XRPC methods under chat.bsky.actor
chat.bsky.convo Call XRPC methods under chat.bsky.convo
chat.bsky.moderation Call XRPC methods under chat.bsky.moderation
com.atproto.admin Call XRPC methods under com.atproto.admin
com.atproto.identity Call XRPC methods under com.atproto.identity
com.atproto.label Call XRPC methods under com.atproto.label
com.atproto.lexicon Call XRPC methods under com.atproto.lexicon
com.atproto.moderation Call XRPC methods under com.atproto.moderation
com.atproto.repo Call XRPC methods under com.atproto.repo
com.atproto.server Call XRPC methods under com.atproto.server
com.atproto.sync Call XRPC methods under com.atproto.sync
com.atproto.temp Call XRPC methods under com.atproto.temp
tools.ozone.communication Call XRPC methods under tools.ozone.communication
tools.ozone.hosting Call XRPC methods under tools.ozone.hosting
tools.ozone.moderation Call XRPC methods under tools.ozone.moderation
tools.ozone.safelink Call XRPC methods under tools.ozone.safelink
tools.ozone.server Call XRPC methods under tools.ozone.server
tools.ozone.set Call XRPC methods under tools.ozone.set
tools.ozone.setting Call XRPC methods under tools.ozone.setting
tools.ozone.signature Call XRPC methods under tools.ozone.signature
tools.ozone.team Call XRPC methods under tools.ozone.team
tools.ozone.verification Call XRPC methods under tools.ozone.verification
Flags:
-h, --help help for call
Use "slink call [command] --help" for more information about a command.
$ slink call com.atproto.sync
Call XRPC methods under com.atproto.sync
Usage:
slink call com.atproto.sync [command]
Available Commands:
get-blob Get a blob associated with a given account. Returns the full blob as original...
get-blocks Get data blocks from a given repo, by CID. For example, intermediate MST node...
get-checkout DEPRECATED - please use com.atproto.sync.getRepo instead
get-head DEPRECATED - please use com.atproto.sync.getLatestCommit instead
get-host-status Returns information about a specified upstream host, as consumed by the serve...
get-latest-commit Get the current commit CID & revision of the specified repo. Does not require...
get-record Get data blocks needed to prove the existence or non-existence of record in t...
get-repo Download a repository export as CAR file. Optionally only a 'diff' since a pr...
get-repo-status Get the hosting status for a repository, on this server. Expected to be imple...
list-blobs List blob CIDs for an account, since some repo revision. Does not require aut...
list-hosts Enumerates upstream hosts (eg, PDS or relay instances) that this service cons...
list-repos Enumerates all the DID, rev, and commit CID for all repos hosted by this serv...
list-repos-by-collection Enumerates all the DIDs which have records with the given collection NSID.
notify-of-update Notify a crawling service of a recent update, and that crawling should resume...
request-crawl Request a service to persistently crawl hosted repos. Expected use is new PDS...
Flags:
-h, --help help for com.atproto.sync
Use "slink call com.atproto.sync [command] --help" for more information about a command.
To make authenticated calls, use SLINK_AUTH to specify the authentication header. To make calls as a PDS admin, set SLINK_AUTH to the Basic Auth credentials for the admin.
export SLINK_AUTH="Basic $(echo -n "admin:$ADMIN_PASSWORD" | base64)"
Now you can make calls as the PDS admin.
$ slink call com.atproto.admin get-invite-codes
{
"codes": [
{
"available": 1,
"code": "...",
"createdAt": "...",
"createdBy": "admin",
"forAccount": "admin"
},
...
]
"cursor": "..."
}
To make calls as a PDS user, set SLINK_AUTH to a Bearer token for the user. Here's one way to do that:
export SLINK_AUTH="Bearer $(slink call com.atproto.server create-session --identifier $HANDLE --password $PASSWORD | jq .accessJwt -r)"
With this, you can make calls as the user.
$ slink call com.atproto.server get-session
{
"active": true,
"did": "did:plc:...",
"email": "...",
"emailConfirmed": true,
"handle": "..."
}
slink can be configured to use a sidecar proxy like IO. This moves authentication and routing to the sidecar and can allow credential owners to keep their secrets secure while allowing these secrets to be used to call specific (allow-listed) XRPC methods.
If the sidecar proxy listens on a TCP socket, configuration is trivial: just set SLINK_HOST to the port where the proxy is listening. If the sidecar proxy listens on a Unix domain socket, it's also really easy: set SLINK_HOST to unix: followed by the socket name, e.g.
export SLINK_HOST=unix:@io-calling-pds
Here @io-calling-pds is the name of a Linux abstract socket that a sidecar provides for calling a PDS.
See the make manifest make target for an example build of slink that only supports the specific functions and records in the sample-manifest.json file. When you provide a manifest, slink automatically finds all the lexicon dependencies of the symbols in the manifest and generates only what is needed for these generated implementations to work. While this might not make sense for slink itself, it's a great way to generate a dependency-minimized client library for use in a Lexicon-based application.
Copyright 2026, Agent IO (Tim Burks).
slink is released under the AGPL.
slink is pre-release software. It could change in significant ways, so before depending on it, get in touch!
-- Tim Burks @timburks.me | @agent.io