HTTP and XRPC API
What knotless-serve answers over HTTP. Which flags turn each part on is
in Commands; how the parts work is in
knotless-serve.
An error answers its status with a plain-text message, but under /xrpc/,
where it is JSON as XRPC has one: {"error", "message"}, error naming
its kind (InvalidRequest, AuthenticationRequired, Forbidden,
NotFound, Conflict, UpstreamFailure, InternalServerError) unless an entry
names another. In a path, <authority> is a handle or DID and <repo> a
repository’s name; a trailing .git on the name is dropped.
| Status | When |
|---|---|
| 400 | a bad parameter, or a bad repository name |
| 401 | a missing or bad service token |
| 403 | a caller who may not do this |
| 404 | <url>: no such repository, <url>: taken down, <name>: no one's handle (not one an account can have, or one a resolver answered that none has), or a part this server does not run (this server keeps no index, this server is not a gateway) |
| 502 | the PDS or identity lookup failed |
Who asks
Section titled “Who asks”A query under Appview or Web data may carry a
service token, as atproto’s appviews take one: Authorization: Bearer <jwt>, signed by the asker’s PDS (com.atproto.server.getServiceAuth)
with this server’s did:web as aud and the query’s NSID as lxm. A
PDS proxying to the #knotless_appview service sends one itself. With
none, a query answers as for anyone. A token that does not hold, or one
sent to a server with no --public-url and so no DID, is 401
AuthenticationRequired. With no token, the cookie knotless_session
of someone logged in to the pages names the asker; one that no longer
holds asks as no one. Every query answer is Vary: authorization, cookie,
and one with an asker is Cache-Control: private, no-cache; one that read
a private repository for its asker, private, no-store.
A repository no one may read publicly is read as the asker, with the
session they logged in to the pages with: a member of it reads it as a
public one, and anyone else, asker or not, gets the 404 NotFound a
repository never made gets (access.md).
GET /<owner>/<name>/raw/<rev>/<path> reads as the cookie’s account too,
and varies by cookie; the cards, git and /api/ routes read as no one.
A query may instead prove its asker a member with a delegation token,
for an app that signs people in to their own PDS rather than here:
Atproto-Space-Delegation: <jwt>, from the asker’s PDS
(com.atproto.space.getDelegationToken) for the repository’s space,
at://<owner DID>/space/dev.knotless.space/<name>, one for each query.
The server checks its signature against the account it names and spends
it: one sent again is 401 AuthenticationRequired, as is one that is no
delegation token, or that names another account than a service token or
cookie sent with it. It reads that one repository as its member, as a
session would, exchanging the token with the space’s authority, which
answers only a member; anything else the query reads, it reads as for
anyone, or as the service token or cookie. A token for another space, or
from someone who is not a member, reads as no token does. The queries
answer a page on any other site (CORS, with Authorization and
Atproto-Space-Delegation allowed), which a browser never sends the
cookie with, and so does /.well-known/did.json, where such a page finds
them (#knotless_appview).
Health
Section titled “Health”GET /healthz
Section titled “GET /healthz”Answers ok while the process runs.
GET /_status
Section titled “GET /_status”repo<handle or did>/<name>: also read that repository’s mirror, as its code page does
Answers 200 when everything holds, else 503, with Cache-Control: no-store. One feed bringing events is enough for ok; with no feeds
followed, feeds pass.
| Field | |
|---|---|
ok | everything holds |
problems | strings, below; dead feeds are listed even while ok is true |
uptime_seconds | since the process started |
feeds[] | name, url, ok, connected, silent_seconds, stale_after_seconds, events, seq |
index | ok, error; with --index |
mirror | repo, ok, error, commit (the default branch’s), packs (.pack files in the mirror); with ?repo= |
A feed never heard from is silent since it was first followed. Its
stale_after_seconds is --stale, else 120.
Problems:
<feed>: not connected<feed>: no event for <n>sno feed is bringing eventsindex: <error>mirror <repo>: <error>, including?repo= takes <handle or did>/<name>
Git smart HTTP
Section titled “Git smart HTTP”| Route | |
|---|---|
GET /<authority>/<repo>/info/refs?service=git-upload-pack | the ref advertisement |
POST /<authority>/<repo>/git-upload-pack | a fetch |
Any other service is 403 read only: push with knotless:// instead.
The Git-Protocol header is passed to git upload-pack, so protocol v0
and v2, --filter and --depth work. A gzip request body
(Content-Encoding: gzip) is decompressed; bad gzip is 400. Answers are
application/x-git-upload-pack-advertisement or -result, Cache-Control: no-cache, not compressed.
Object API
Section titled “Object API”Under /api/<authority>/<repo>. A failed read of a tree, blob, log, diff
or object is 404; a failure to list the refs is 500.
| Route | Parameters | Answer |
|---|---|---|
GET /refs | {refs: [{name, target}], head} | |
GET /tree | rev, path | {rev, path, entries: [{name, mode, type, sha, size}]} |
GET /blob | rev, path | the raw bytes, application/octet-stream, ETag the blob’s hash |
GET /log | rev, path, limit (default 50, at most 1000) | {rev, commits: [{sha, parents, author: {name, email, date}, message}]} |
GET /diff | base, head | git diff base...head, text/x-diff; charset=utf-8 |
GET /object/<sha> | the raw object, application/octet-stream, its type in X-Git-Type, ETag, immutable |
rev, base and head default to HEAD. An empty one, one starting
with -, or one holding a NUL, newline or space is 400 bad revision "<rev>".
path is trimmed of slashes. <sha> is 40 or 64 hex characters, else
400 not an object id: <sha>. A tree or blob read at a 40-character hash is
public, max-age=31536000, immutable, and otherwise no-cache.
Appview
Section titled “Appview”XRPC queries under /xrpc/. All but getProfile, describeServer and getStats need --index, and
without one answer 404 this server keeps no index.
GET dev.knotless.listRepos
Section titled “GET dev.knotless.listRepos”| Parameter | Default | |
|---|---|---|
q | matched against name and description | |
runner | a DID: only repositories whose record names it in runners | |
triager | a DID: only repositories whose record names it in triagers | |
topic | only repositories whose record names it in topics | |
limit | 50 | clamped to 1 to 500 |
cursor | from the page before |
Answers repos (did, name, description?, forkOf?, createdAt,
owner), newest first, and cursor when the page was full. Taken-down
owners and repositories are left out. Asked as an account, the first page
with no runner, triager or topic starts with the private repositories
matching q that it may read here, at most limit more, each private:
its own, and those it was proven a member of, as it read one or as it
was asked after in the background. The answer is then that account’s
alone.
Lexicon
Public repositories an appview has indexed, newest first. No authentication needed; asked as an account, with a service token or the pages' session cookie, the first page of a search with no runner, triager or topic starts with the private repositories that account may read on this appview that match too, each marked private, and the answer is that account's alone.
Parameters
qstring, at most 100 bytes, optional | Only repositories whose name or description holds this text, ignoring case. |
topicstring, at most 50 bytes, optional | Only repositories whose records name this topic, as rust. |
runnerstring (did), optional | Only repositories whose record names this account among its runners: how a runner finds the repositories it is to run. |
triagerstring (did), optional | Only repositories whose record names this account among its triagers: how a triager finds the repositories it is to triage. |
limitinteger, 1 to 500, default 50, optional | |
cursorstring, at most 512 bytes, optional |
Output
cursorstring, at most 512 bytes, optional | |
reposarray of repoView |
GET dev.knotless.searchOwners
Section titled “GET dev.knotless.searchOwners”| Parameter | Default | |
|---|---|---|
q | required; matched against the handle, ignoring case | |
limit | 10 | clamped to 1 to 50 |
Answers owners, each an owner (did, handle, avatar?) and its
repoCount: the accounts owning repositories listRepos lists, those whose
handle starts with q first, then the one with the newest repository. Owners
are matched by the handles the index keeps; an owner whose handle was never
looked up is looked up first, once, and kept.
Lexicon
Accounts owning public repositories an appview has indexed, whose handle holds the query, ignoring case: those it starts with first, then by their newest repository. No authentication; responses are not personalized.
Parameters
qstring, at most 100 bytes | Text the handle holds. |
limitinteger, 1 to 50, default 10, optional |
Output
ownersarray of ownerView |
GET dev.knotless.describeServer
Section titled “GET dev.knotless.describeServer”No parameters. Answers runners, each a did: the one
--default-runner names, which a client names in a new public
repository’s record unless its owner names others. Empty when it names
none. It needs no index.
Lexicon
What this appview offers the clients that use it: the runners a new public repository names unless its owner names others, and the oldest client version it asks for. No authentication; responses are not personalized.
Output
runnersarray of repo#runner, at most 32, optional | The accounts a new public repository's record names in runners unless its owner names others: a runner this appview's operator keeps for whoever names it. Absent or empty, it names none. |
clientclient, optional | The oldest client version this appview asks for. Absent, it asks for none. |
GET dev.knotless.getForge
Section titled “GET dev.knotless.getForge”| Parameter | Default | |
|---|---|---|
repo | the repository’s at:// URI; only its owner’s DID is used | |
key | the repository’s key | |
limit | 500 | clamped to 1 to 1000 |
cursor | the last URI of the page before |
Answers entries (uri, author, value), every record naming the
repository, sorted by URI, less what moderation removes, and cursor only
when more remain. Records about the repository it was transferred from
are included when both owners agree on the move.
Lexicon
Every pull, issue, comment, state change, hide and block anyone has written about a public repository, as an appview has indexed them from the firehose, less what its owner and the appview keep out. A page at a time, ordered by URI: follow cursor until it is absent. No authentication; responses are not personalized. Private repositories are never indexed: their readers list the members' records in the space.
Parameters
repostring (at-uri) | The repository's dev.knotless.repo record; only its owner is matched. |
keystring (tid) | The repository's key. Records naming it are returned whatever name their URI has, with those naming a repository it was moved from with its owner's agreement. |
limitinteger, 1 to 1000, default 500, optional | |
cursorstring (at-uri), optional | The last URI of the page before. |
Output
cursorstring (at-uri), optional | Absent on the last page. |
entriesarray of entry |
GET dev.knotless.listChecks
Section titled “GET dev.knotless.listChecks”| Parameter | Default | |
|---|---|---|
repo, key | as for getForge | |
ref | only checks of this ref | |
commit | only checks of this commit | |
pull | only checks of this pull’s at:// URI | |
limit | 50 | clamped to 1 to 100 |
cursor | from the page before |
Answers checks (uri, runner, value), newest startedAt first, and
cursor when there may be more. Only the runners the repository record
names now count.
Lexicon
The dev.knotless.check records written about a public repository by the accounts its record names in runners, newest run first; checks by any other account, and anything the appview has taken down, are left out. The runners read are those of the repository's record as the appview has it now. A page at a time: follow cursor until it is absent. No authentication; responses are not personalized.
Parameters
repostring (at-uri) | The repository's dev.knotless.repo record; only its owner is matched. |
keystring (tid) | The repository's key. |
refstring, at most 1024 bytes, optional | Only checks of this ref, named in full, such as refs/heads/main. |
commitstring, at most 64 bytes, optional | Only checks of this commit, its hex object ID. |
pullstring (at-uri), optional | Only the runs of this pull's head. |
limitinteger, 1 to 100, default 50, optional | |
cursorstring, at most 2048 bytes, optional | The cursor of the page before. |
Output
cursorstring, at most 2048 bytes, optional | Absent on the last page. |
checksarray of check |
GET dev.knotless.getFeed
Section titled “GET dev.knotless.getFeed”actoronly what this DID did
filterfollowing: what the account asking and those it follows did, and anything done on its repositories and those it starredkindonly
pushes(refs pushed and deleted),pullsorissues(opened and moved between states),comments,starsorrepos(created and forked); a kind it does not know answers nothinglimitclamped to 1 to 100
cursorfrom the page before
Answers events (createdAt, actor {did, handle, avatar?}, repo
{did, handle, name}, action) and cursor. action.$type is one of:
$type | Fields |
|---|---|
dev.knotless.getFeed#created | description?, forkOf? |
dev.knotless.getFeed#pushed | ref, created, to?, forced?, count, new, commits (sha, message, merge?) |
dev.knotless.getFeed#deleted | ref |
dev.knotless.getFeed#opened | subject, title |
dev.knotless.getFeed#commented | subject, title, text, verdict? |
dev.knotless.getFeed#changed | subject, title, state |
dev.knotless.getFeed#starred | subject, the repository record |
A push’s actor is the account that pushed: a ref’s claim or record that
names a pusher the repository lists among its collaborators or runners
is theirs, and any other is the owner’s. actor and filter=following
choose pushes by that account too. Pushes to one ref by one account within
an hour of each other are one event. to is the commit the last of them left
the ref at, which a page links to as the ref may since have been deleted. A push walks
at most 100 commits and lists at most 10; count is what it brought, and
new those no other ref had first. forced is a push that left commits
behind, as a rebase or a rewrite does: its count and new leave out the
commits it brought again, those by the same author at the same time with
the same message as one it left behind, so a rebase alone counts none.
changed is a pull or issue whose state changed to open, closed or
merged, and nothing else: labels, the merge queue and a pull pointed at
another branch are not in the feed.
starred comes only with actor (the stars it gave) or filter=following
(the stars of the asker and those it follows). Everyone’s feed has none,
since anyone can write as many stars as they like. filter=following with
no one logged in or no service token answers 401. A commit with more than
one parent says merge: true.
Lexicon
What happened on the public repositories an appview has indexed, newest first: repositories created and forked, refs pushed and deleted, pulls and issues opened, discussed and moved between states, and repositories starred. With filter following, what the account asking and those it follows did, and anything done on its repositories and those it starred; without, the same for anyone, but no stars. Without actor, what a repository's bots said or pushed on it is left out: the runners and triagers its record names, but its owner and collaborators. A pull a runner merged landing the merge queue is shown as merged by whoever queued it.
Parameters
limitinteger, 1 to 100, default 40, optional | |
cursorstring, optional | Where the page starts: a previous page's cursor. |
actorstring (did), optional | Only what this account did: the repositories it created, the refs it pushed, the pulls, issues, comments and changes of state it wrote, and the repositories it starred. |
filterstring, everyone, following, default "everyone", optional | Whose doings: everyone's, or with following, those of the account asking and whom it follows, which needs an account to ask. |
kindstring, pushes, pulls, issues, comments, stars, repos, optional | Only what is of this kind: refs pushed and deleted, pulls or issues opened and moved between states, comments, repositories starred, or repositories created and forked. A kind the appview does not know answers nothing. |
Output
eventsarray of eventView | |
cursorstring, optional | Where the next page starts, when there may be one. |
GET dev.knotless.getActorActivity
Section titled “GET dev.knotless.getActorActivity”| Parameter | Default | |
|---|---|---|
actor | the account’s DID | |
since | a year before now | the first day counted is the one this time falls on in tz |
tz | UTC | the IANA time zone whose days are counted, as America/New_York; one it does not know is UTC |
Answers days, oldest first, each a date in tz (day, as YYYY-MM-DD) on
which the account did something, with how many repositories it made
(repos), refs it moved (pushes, one for each claim it is credited
with as getFeed credits them, on its repositories or others’), and pulls, issues
and comments it wrote; and repos, each of its repositories ever pushed
to, by name, with when it last was (pushedAt) and its days
(day, pushes, by anyone) since since. An account or repository taken down counts
for nothing.
Lexicon
How much an account did on each day since a time, by kind, as an appview has indexed it, and its repositories' pushes: when each was last pushed to, and how often on each of those days. Days are dates in the zone tz, and only days with something done are listed. Repositories and accounts taken down count for nothing. No authentication; responses are not personalized.
Parameters
actorstring (did) | The account. |
sincestring (datetime), optional | The first day counted is the one this falls on in tz; a year before now if not given. |
tzstring, at most 64 bytes, optional | The IANA time zone whose days are counted, as America/New_York; UTC if not given or not known. |
Output
daysarray of dayView | Oldest first. |
reposarray of repoActivity | Each of the account's repositories ever pushed to, by name. |
GET dev.knotless.getProfile
Section titled “GET dev.knotless.getProfile”| Parameter | |
|---|---|
actor | a handle or DID |
Needs no index. Answers did, displayName?,
description?, pronouns?, links?, avatar?, bot? (true when its
dev.knotless.profile says it is a bot); an avatar served here
is made absolute from X-Forwarded-Proto (default http) and Host.
With an index, also badges?: each badge the account earned (kind,
tier from 1 to 4, or 1 for a badge earned once, count, and next,
the count the next tier starts at, absent at the top), in the order the pages show them
(badges). An
account that cannot be reached is 400 {"error": "ProfileNotFound", "message": ...}.
Lexicon
How an account shows on knotless: its dev.knotless.profile, field by field over its app.bsky.actor.profile, with its follows as the appview counts them. Optional authentication: with an asker, whether it follows the account.
Parameters
actorstring (at-identifier) | The account, by DID or handle. |
Output
profileView
Errors
ProfileNotFound
GET dev.knotless.getStats
Section titled “GET dev.knotless.getStats”No parameters, and no sign-in: what the /stats page shows. days, the
last 30 (UTC), oldest first, each with visitors, views and, from the
index, repos, pushes, pulls, issues, comments, checks and
authors; and network?, as dev.knotless.getUsage has it, absent
without an index. Days are zero without --usage. Cached for five minutes
(Cache-Control: public, max-age=300). What only operators read, the
requests, routes, events, slow statements, failures and crawlers, is dev.knotless.getUsage’s
(deploy).
Lexicon
What an appview shows anyone of how it is used: the network as its index has it now, and each of the last 30 days, what the network wrote in it and how many people drew its pages (by a hash forgotten each day). Its operators read the rest with dev.knotless.getUsage.
Output
daysarray of day | The last 30 days, oldest first, today (UTC) the last. |
networkgetUsage#network, optional | Absent where the appview keeps no index. |
The pages follow what happens as it happens as server-sent events (serve). Each answers as the query for the same thing does for the same viewer, a 404 included, lasts ten minutes at most, and ends as the server stops; a browser then asks again by itself.
- GET
/_live/repo Events
changed, its datanull, a quarter of a second after the index takes in or lets go a record about the repository, once for those that came meanwhile- GET
/_live/check Events
output: what the run printed since, a JSON string, itsidthe byte where it ends in all the run printed;steps: the steps that ended, as the record has them, each time more do;going: the name of the step going, a JSON string, or null between steps;ended, its datanull, once, as the run’s steps are over
Moderation
Section titled “Moderation”These need --index and --public-url. The admin methods, and
dev.knotless.getUsage, take a service token for the method,
Authorization: Bearer, from an operator: an account
KNOTLESS_ADMIN_DIDS names. With no token, they take the session cookie
of an operator logged in to the pages, asking from the site’s own origin,
as the /admin page does. Without either, from another site, or with a
token for another method or server, they answer 401; from anyone else,
403 <did> is not an operator here.
- POST
com.atproto.moderation.createReport Answer the stored report:
id,reasonType,subject,reportedBy,createdAt,reason?- GET
dev.knotless.getLogs Answer
lines, oldest first, each{line}, the JSON line as logged; 400 asking for none of them, 404 keeping no logsLexicon
The lines an appview logged, for its operators, oldest first: all of one request's by its trace, or the last of a message, a level or a duration. It takes a service token for this method from one of the accounts it names as its operators, or the session cookie of one logged in to its pages. Kept across restarts, at most 30 days. Each read is itself logged, with who asked.
Parameters
tracestring, optionalAll of one request's lines, by the trace ID each carries and its answer's x-trace-id names. messagestring,request,outgoing,sql,git,mcp tool failed,logs read, optionalOnly lines with this message. levelstring,error,warn,info,debug,trace, optionalOnly lines at this level or above. minMsinteger, optionalOnly lines that took this many milliseconds or more. limitinteger, 1 to 500, optionalHow many, the last of those asked for. Unless given, 500 for a trace, so a request's lines come whole, and 100 otherwise. Output
linesarray of lineThe lines asked for, oldest first. - GET
dev.knotless.listReports Answer
reports, newest first, andcursorwhen the page is fullLexicon
The reports a moderation service was sent (com.atproto.moderation.createReport), newest first. For its operators: it takes a service token for this method from one of the accounts it names as its operators, or the session cookie of one logged in to its pages.
Parameters
limitinteger, 1 to 500, default 50, optionalcursorstring, optionalA report id: those before it. Output
cursorstring, optionalreportsarray of report- POST
com.atproto.admin.updateSubjectStatus Answer
subject,takedown- GET
com.atproto.admin.getSubjectStatus Answer
subject,takedown.applied- GET
com.atproto.label.queryLabels Answer
labels- GET
com.atproto.label.subscribeLabels Answer a WebSocket of labels
Invites
Section titled “Invites”A private repository’s owner leaves its runners an invite, and a runner
reads its own, each with a service token for the method from their own
PDS: knotless-run’s --discover finds private repositories so
(runner). These need
--index and --public-url, as the methods above do, and answer 401
without a token for the method.
- POST
dev.knotless.inviteRunner Answer
{}; an invite already there moves to the endLexicon
Tells a runner, through the appview, that the caller's private repository names it among its runners; or a triager, among its triagers. Nothing written in a space reaches the firehose, so a runner learns of a private repository only so, from dev.knotless.listRunnerInvites; it then reads the repository's record, as a member of its space, before running anything. Inviting it again moves the invite to the end of its list. Requires a service token from the caller's PDS, audience the appview's DID, for this method.
Input
runnerstring (did)The runner, or triager, invited. namestring, at most 100 bytesThe repository's name: its record key in the caller's space. - GET
dev.knotless.listRunnerInvites Answer the caller’s
invitesaftercursor, oldest first, eachdid,name,createdAt; andcursor, where the page ended, the one given when it is emptyLexicon
The invites dev.knotless.inviteRunner left for the caller, oldest first: the private repositories that say they name it among their runners, or triagers. Requires a service token from the caller's PDS, audience the appview's DID, for this method.
Parameters
limitinteger, 1 to 500, default 100, optionalcursorstring, at most 32 bytes, optionalThose after the invite a page ended at. Output
cursorstring, at most 32 bytes, optionalWhere this page ended: the cursor to ask from for invites after it, kept even when it is the last. invitesarray of invite
Secrets
Section titled “Secrets”An owner leaves a repository’s runners secrets, each sealed to one
runner’s dev.knotless.runnerKey, so this server holds only ciphertext
(runner). A repository is named by
its owner, the caller, and its key, never its name, so a rename keeps
its secrets. Each method takes a service token for itself, from the
caller’s PDS, or the pages’ login, as their writes take it (403 from
another site), else 401, and needs --index. The pages seal each value
in the browser, to the keys listRunners gives, so this server never
sees one.
- POST
dev.knotless.putSecret Answer
{}; one of the same repository, runner and name is replacedLexicon
Leaves a secret for one of the caller's repositories with one of its runners, sealed to the runner's dev.knotless.runnerKey so the appview holds only ciphertext. The caller is the repository's owner. One there already under the same repository, runner and name is replaced. Requires a service token from the caller's PDS, audience the appview's DID, for this method, or the appview's own pages' login, as their writes take it.
Input
repostring, at most 32 bytesThe repository's key, as its dev.knotless.repo record holds it: a rename keeps it. runnerstring (did)The runner it is sealed to. namestring, at most 64 bytesThe secret's name, as a workflow's secretslists it: a shell variable's name, not one starting KNOTLESS_RUN.refsarray of listSecrets#refPattern, at most 16, optionalThe refs whose pushes it reaches, as a trigger's refsmatch them; absent for every push.keystring (did)The runner's key it is sealed to, a did:key. sealedbytesThe value sealed with HPKE to key, bound to the caller,repo,runner,nameandrefs: the encapsulated key, then the ciphertext.- POST
dev.knotless.deleteSecret Answer
deleted, how many of the caller’s wentLexicon
Deletes the secrets dev.knotless.putSecret left for one of the caller's repositories: those of a name, those sealed to a runner, or both; with neither, all of the repository's. Requires a service token from the caller's PDS, audience the appview's DID, for this method, or the appview's own pages' login, as their writes take it.
Input
repostring, at most 32 bytesThe repository's key, as its dev.knotless.repo record holds it. namestring, at most 64 bytes, optionalOnly the secret of this name, for every runner. runnerstring (did), optionalOnly those sealed to this runner. Output
deletedintegerHow many were deleted, one for each runner a name was sealed to. - GET
dev.knotless.listSecrets Answer
secrets, by name then runner, eachname,runner,refs,key,updatedAt: every one to the owner, withsealedonly on those sealed to it; those sealed to the caller, withsealed, to anyone elseLexicon
The secrets dev.knotless.putSecret left for a repository, by name. Its owner sees every one, with the value only of those sealed to it, as a runner that logs in as the owner. A runner sees those sealed to it, with the sealed value, to open with its key; anyone else, none. Requires a service token from the caller's PDS, audience the appview's DID, for this method, or the appview's own pages' login, as their writes take it.
Parameters
ownerstring (did)The repository's owner. repostring, at most 32 bytesThe repository's key, as its dev.knotless.repo record holds it. Output
secretsarray of secret
A subject is a com.atproto.admin.defs#repoRef or a
com.atproto.repo.strongRef, else 400. getSubjectStatus with neither
parameter is 400 com.atproto.admin.getSubjectStatus takes a did or a uri.
updateSubjectStatus with takedown.applied: true takes the subject down
and emits a !takedown label; with false it restores it and emits the
label with neg: true.
queryLabels answers this server’s own standing takedowns, at most 250
per pattern, each with ver: 1, src the server’s DID and cts the
takedown’s time. subscribeLabels sends #labels frames in CBOR (header
{op: 1, t: "#labels"}, body {seq, labels: [label]}): with a cursor,
the labels after it first, then live ones. Labels are unsigned.
Space notifications
Section titled “Space notifications”A private repository’s space’s authority, the owner’s PDS, tells the server
of writes there once asked to (access).
Each is signed as the space’s owner for the server’s #knotless_appview
service and this method, else 401; a space not the signer’s own, or not a
private repository’s, is 403. Both need a gateway DID and a server people
log in to, else 404.
- POST
com.atproto.space.notifyWrite Answer
{}; the space is synced now, and followed first as its owner if they are logged in here and it is not yet- POST
com.atproto.space.notifySpaceDeleted Answer
{}; the space is forgotten, with every record it gave
Runs as they go
Section titled “Runs as they go”A runner sends the server what its runs print as they print it, and asks
it which of its private repositories’ spaces were written to
(runner), each with a
service token for the method from its own PDS, else 401; a token is
checked once a minute at most. These need --index and --public-url, as
invites do.
- POST
dev.knotless.putRunOutput Answer
{watched}: whether a page follows the run now; 429 while the server holds 1,024 runs, or 128 of the caller’sLexicon
Sends the appview what a check's steps printed since the runner last sent, for the pages following the check while it runs: the dev.knotless.check record says nothing of a run until it ends. Only whole lines are sent, with the run's secrets masked as its log's are. The appview keeps the tail of each run in memory, never for good: the record's log is what is kept. Only the runner that files the check may send its output, and an appview holding as many runs as it keeps, or as many of the caller's, refuses more until some end. Requires a service token from the caller's PDS, audience the appview's DID, for this method.
Input
checkstring (at-uri)The dev.knotless.check record the run files, in the caller's repo. offsetintegerWhere textstarts in all the run has sent, in bytes: text sent again is passed over, and a gap, from output the runner let go, starts what is kept anew.startedAtstring (datetime), optionalWhen the run started, as its check record says. A run of the same check that started at another time, as a rerun at the same commit is, starts what is kept anew. textstring, at most 20000 bytes, optionalWhat the steps printed since the last send, as UTF-8. stepsarray of check#step, at most 256, optionalThe steps that ended so far, as the check's record will say. goingstring, at most 512 bytes, optionalThe name of the step running now, sent with steps: none between steps.endedboolean, optionalSet on the last send: the run's steps are over, and its record is about to be filed. Output
watchedbooleanWhether a page follows the run now: a runner may send less often when none does. - GET
dev.knotless.awaitSpaceWrites Answer
cursor, andwrites, eachspace: those written to aftercursorof the spaces whose owners invited the caller, as soon as there is one, else none after 25 seconds; with no cursor, or one of another start of the server or past the 4,096 writes it remembers, at once, none, andmissed: trueLexicon
The spaces written to since
cursor, of the private repositories whose owners invited the caller (dev.knotless.inviteRunner), as their authorities told the appview (com.atproto.space.notifyWrite): nothing written in a space reaches the firehose. Answers as soon as there is one, or with none after a while, to be asked again from the cursor it gives. A write told of is only a reason to look: the caller reads the space itself, as a member. Requires a service token from the caller's PDS, audience the appview's DID, for this method.Parameters
cursorstring, at most 64 bytes, optionalWhere the last answer ended. With none, the answer says only where things are now. Output
cursorstring, at most 64 bytesWhere this answer ended: the cursor to ask from next. writesarray of writemissedboolean, optionalSet when cursorwas none, or of no place the appview remembers, as after it restarted: any space may have been written to since.
Gateway
Section titled “Gateway”These need --public-url, and otherwise answer 404 this server is not a gateway. Each XRPC call carries a service token with the gateway’s DID as
audience and the method as lxm. Gateway
describes each.
| Route | Input | Answer |
|---|---|---|
GET /.well-known/did.json | the did:web document, with #knotless_gateway, #atproto_labeler and #knotless_appview services | |
GET /oauth-client-metadata.json | the gateway’s OAuth client metadata | |
GET /oauth/callback | the authorization server’s redirect | a page saying whether the gateway now writes for you |
GET /oauth/bot-client-metadata.json | the bots’ OAuth client metadata; with --bots | |
GET /oauth/bot | account, a handle or DID that KNOTLESS_BOT_DIDS names | a redirect to approve a bot’s login at its PDS; with --bots |
GET /oauth/bot-callback | the authorization server’s redirect | a page saying whether the bot is logged in; with --bots |
POST dev.knotless.putDelegation | {url, state} | |
GET dev.knotless.getDelegation | state | {done} |
POST dev.knotless.pushRefs | a JSON header line {repo, name, updates: [{dst, src?, force?}]}, then a pack; application/x-knotless-push, at most 512 MiB | {results: [{name, error?}]} |
POST dev.knotless.createCommit | {repo, name, branch, message, author?: {name, email}, base?, changes: [{path, data?, executable?}]} | {sha, parent?, changed, batched} |
A change with no data deletes its path.
MCP over HTTP
Section titled “MCP over HTTP”Served with --public-url, to any account the index has not taken down. The server is its own OAuth authorization server, with
PKCE.
| Route | |
|---|---|
/mcp | the MCP tools, as streamable HTTP with JSON answers and no sessions; takes a bearer token |
GET /.well-known/oauth-protected-resource, /.well-known/oauth-protected-resource/mcp | protected-resource metadata |
GET /.well-known/oauth-authorization-server | authorization-server metadata |
POST /mcp/register | client registration (RFC 7591) |
GET, POST /mcp/authorize | sign-in, with atproto OAuth at the user’s PDS |
GET /mcp/oauth/callback | where the user’s PDS sends them back |
POST /mcp/token | tokens |
GET /mcp/oauth-client-metadata.json | the client metadata it signs users in with |
Registrations and tokens are signed, not stored.
| Lifetime | |
|---|---|
| Access token | 1 hour |
| Refresh token | 90 days |
| An account’s session, from its last token | 90 days |
| Client registration | 100 years |
| Sign-in | 15 minutes |
| Authorization code | 5 minutes |
| Limit | |
|---|---|
| Tool calls running, per account | 8 |
Of those, with a wait | 2 |
| Tool calls started a minute, per account | 120 |
| Sign-ins waiting on PDSes, from everyone | 1000 |
A tool call past an account’s limits answers as a tool error, and a sign-in past the server’s as a 503 page.
Releases
Section titled “Releases”With --releases <dir> or --releases knotless://<owner>/<name>:
- GET
/install.sh the install script,
text/x-shellscript; charset=utf-8, pointed at--public-url, else atX-Forwarded-Proto(defaulthttp) andHost(default127.0.0.1:2590)- GET
/releases/... the directory:
latest, and<version>/knotless-<version>-<target>.tar.gzwith its.sha256
From a repository, the same paths answer from its releases of the
knotless package (packages.md), listed again
each minute:
- GET
/releases/latest the latest version and a newline; 404
no releasewith none- GET
/releases/<version>/<file> the file,
application/octet-stream, read once from the owner’s PDS, checked against its record, then kept under the cache directory’sserve/releases/by its SHA-256- GET
/releases/<version>/<file>.sha256 <sha256> <file>and a newline, from the record
A version or file the releases do not have is 404. A repository that cannot be read, or a file that cannot be read or kept, is 502 with the error.
Images
Section titled “Images”An OCI registry of the images repositories publish as releases, with
knotless publish --oci or a push here
(packages.md). An image is named
<owner>/<repo>/<package>, or <owner>/<repo> for the package named as
the repository, the owner by handle; a tag is a version, or latest, the
highest without a pre-release part.
- GET
/v2/ with any bearer token,
{}andDocker-Distribution-API-Version: registry/2.0; without one, 401 asking for one:WWW-Authenticate: Bearer realm="<this server>/v2/token",service="knotless"- GET
/v2/token a token:
{token, access_token},anonymouswith no credentials, which pulls need; with Basic credentials, a handle (or DID) and an app password, one to push with for an hour,expires_in3600, made by logging in to their PDS and kept only in memory; a password the PDS refuses is 401GET,HEAD /v2/<name>/manifests/<tag or digest>the manifest: by tag, the one its release’s
index.jsonnames; by digest, the file of that digest among the package’s releases; as the media type it says (an OCI image manifest if none), withDocker-Content-DigestGET,HEAD /v2/<name>/blobs/<digest>the blob,
application/octet-stream, withDocker-Content-DigestandContent-Length, sent from where it is kept as it is read: a release’s, or one the owner pushed and no release holds yet- GET
/v2/<name>/tags/list {name, tags}: the versions that are images, highest first, andlatestif there is one- POST
/v2/<name>/blobs/uploads/ begins an upload: 202 with
LocationandDocker-Upload-UUID; with?digest=, the body is the whole blob, ended as aPUTends one- PATCH
/v2/<name>/blobs/uploads/<id> the body as the upload’s next part: 202 with
Range: 0-<last byte>- PUT
/v2/<name>/blobs/uploads/<id>?digest=<digest> the body as its last part: checked against the digest, uploaded to the owner’s PDS and kept; 201 with
LocationandDocker-Content-Digest- PUT
/v2/<name>/manifests/<digest or version> a manifest, at most 4 MiB of JSON: by digest, kept as a blob; by a version, the release of it, with an
index.jsonnaming it and every blob it reaches, an index’s manifests and theirs included
Each file is read from the owner’s PDS once, checked against its record,
and kept by its SHA-256, as the releases are; each package’s releases are
listed again each minute. Errors are the spec’s,
{"errors": [{"code", "message"}]}: 404 NAME_UNKNOWN for a name that is
not <owner>/<repo>[/<package>], an owner that cannot be found, or a
repository that cannot be read, NAME_INVALID for a repository’s name
knotless does not take, MANIFEST_UNKNOWN, BLOB_UNKNOWN and
BLOB_UPLOAD_UNKNOWN; 400 DIGEST_INVALID for an upload ended with no
digest or the wrong one (which ends it), or a manifest put by a digest
not its own, TAG_INVALID for a tag that is not a version (latest
among them), MANIFEST_INVALID and MANIFEST_BLOB_UNKNOWN for a blob or
manifest it reaches that was not pushed; 401 UNAUTHORIZED for a push
with no token, asking for one with scope="repository:<name>:push,pull";
403 DENIED for a push by anyone but the owner, or of a version
published already; 405 UNSUPPORTED for anything else posted, patched
or put; 502 UNKNOWN for what cannot be read, uploaded or kept.
Web data
Section titled “Web data”The pages read what they show from these XRPC queries, as atproto’s
appviews answer theirs: each has its lexicon under
lexicons/dev/knotless, with the views
they share in dev.knotless.defs; the pages’ TypeScript types are made
from them by @atproto/lex, into web/src/lib/lexicons (npm run lex). A
repository is named by its record’s at:// URI, repo, whose authority
may be a handle; an old name, or an owner it moved from, answers under the
name it has now.
- GET
dev.knotless.listRepos?q= repositories matching
q(above)Lexicon
Public repositories an appview has indexed, newest first. No authentication needed; asked as an account, with a service token or the pages' session cookie, the first page of a search with no runner, triager or topic starts with the private repositories that account may read on this appview that match too, each marked private, and the answer is that account's alone.
Parameters
qstring, at most 100 bytes, optionalOnly repositories whose name or description holds this text, ignoring case. topicstring, at most 50 bytes, optionalOnly repositories whose records name this topic, as rust. runnerstring (did), optionalOnly repositories whose record names this account among its runners: how a runner finds the repositories it is to run. triagerstring (did), optionalOnly repositories whose record names this account among its triagers: how a triager finds the repositories it is to triage. limitinteger, 1 to 500, default 50, optionalcursorstring, at most 512 bytes, optionalOutput
cursorstring, at most 512 bytes, optionalreposarray of repoView- GET
dev.knotless.searchOwners?q= the owners of repositories whose handle holds
q(above)Lexicon
Accounts owning public repositories an appview has indexed, whose handle holds the query, ignoring case: those it starts with first, then by their newest repository. No authentication; responses are not personalized.
Parameters
qstring, at most 100 bytesText the handle holds. limitinteger, 1 to 50, default 10, optionalOutput
ownersarray of ownerView- GET
dev.knotless.getActorRepos?actor= an account’s public repositories, each with the
languagemost of it is written in: from the index while a feed is followed, else as its PDS lists them; to the account itself, asking, its private ones too, eachprivate, and to a member asking those it was proven a member of; asked as the account itself, apart, asshared, the private repositories of others it was proven a member ofLexicon
Every repository an account's PDS holds, newest first, as its dev.knotless.repo records say now rather than as an appview has indexed them. No authentication needed; asked as the account itself, with a service token or the pages' session cookie, its private repositories are listed too, and the answer is that account's alone.
Parameters
actorstring (at-identifier)The account, by DID or handle. Output
reposarray of defs#repoViewsharedarray of defs#repoView, optionalAsked as the account itself: the private repositories of other accounts it was proven a member of on this server, newest first, each private. Left out otherwise, or when there are none. Errors
ProfileNotFound- GET
dev.knotless.getRepo?repo= a repository: its owner, description,
links(only http and https ones),topics, default branch, counts, languages,collaborators(the first twenty its record names, besides its owner) andcollaboratorCount,privateif it is, andarchivedAtif its owner archived it; with an asker,viewer: itsstar, atransferoffered to it, and thelabelsit may put on the repository’s pulls and issuesLexicon
A repository, under the name it has now: its record, its default branch, and how many pulls and issues are open. No authentication needed; asked as an account, with a service token or the pages' session cookie, a private repository it is a member of is read too, and the answer is that account's alone.
Parameters
repostring (at-uri)The repository's dev.knotless.repo record. Its authority may be a handle; a name the repository had before, or an owner it moved from with its agreement, finds it under the name it has now. Output
defs#repoViewDetailedErrors
NotFound- GET
dev.knotless.listReleases?repo=&package=&limit=&cursor= its releases (packages.md), newest first, 30 a page by default, of one
packageif given: each withlatestwhen it is its package’s latest,imagewhen its files are an OCI layout’s, its notes as Markdown and ashtml, and its files’ names, sizes and SHA-256; and every package it publishes, by name, with its latest version, how many releases it has and whether it is an image. None for a private repository or without an indexLexicon
A repository's releases, of every package or of one, newest first, a page at a time: follow
cursoruntil it is absent. Each marks whether it is its package's latest, and whether it is a container image. None for a private repository, which cannot publish, or from an appview that keeps no index. No authentication needed.Parameters
repostring (at-uri)The repository's dev.knotless.repo record. Its authority may be a handle; a name the repository had before, or an owner it moved from with its agreement, finds it under the name it has now. packagestring, at most 214 bytes, optionalOnly this package's releases; every package's if absent. limitinteger, 1 to 100, default 30, optionalcursorstring, at most 32 bytes, optionalHow many the pages before held. Output
releasesarray of defs#releaseViewpackagesarray of defs#packageView, optionalEach package the repository publishes, by name, whatever packageasked for, for a filter to name. Absent when it publishes none.cursorstring, optionalAbsent on the last page. Errors
NotFound- GET
dev.knotless.getTree?repo=&ref=&path= a folder, its README and the newest checks of its last commit
Lexicon
A directory at a revision: its entries, folders first, the commit that last changed it, its README, and what the repository's runners report of the revision. Nothing at all, for a repository never pushed. No authentication needed; asked as an account, with a service token or the pages' session cookie, a private repository it is a member of is read too, and the answer is that account's alone.
Parameters
repostring (at-uri)The repository's dev.knotless.repo record. Its authority may be a handle; a name the repository had before, or an owner it moved from with its agreement, finds it under the name it has now. refstring, at most 1024 bytes, optionalA branch, a tag or a commit; the default branch if absent. It may run on into the path, as a web address names them: the longest branch or tag that starts it is the ref, and what follows goes before path.pathstring, at most 4096 bytes, optionalA path in the tree, from its root. Output
refstringThe ref or commit shown, as refnamed it, less any path.pathstringThe directory, from the root; empty for the root. emptyboolean, optionalTrue for a repository never pushed, whose tree is nothing. lastCommitdefs#commitView, optionalThe newest commit that changed the directory. entriesarray of entryreadmereadme, optionalchecksarray of defs#checkViewThe newest run of each workflow at lastCommit, by the runners the repository names: the revision shown, at the root.languagesarray of defs#languageViewWhat the repository is written in, most first: at the root of its default branch, and nowhere else. Errors
NotFound- GET
dev.knotless.getLastCommits?repo=&ref=&path= the newest commit to change each entry of a folder, and at the root, how many commits the ref’s history holds
Lexicon
The newest commit to change each entry of a directory at a revision, as its history shows it: a commit that left an entry as one of its parents had it did not change it. Asked apart from dev.knotless.getTree, as finding them can walk a long history. No authentication needed; asked as an account, with a service token or the pages' session cookie, a private repository it is a member of is read too, and the answer is that account's alone.
Parameters
repostring (at-uri)The repository's dev.knotless.repo record, as dev.knotless.getTree takes it. refstring, at most 1024 bytes, optionalA branch, a tag or a commit, which may run on into the path, as dev.knotless.getTree takes it; the default branch if absent. pathstring, at most 4096 bytes, optionalA directory in the tree, from its root. Output
refstringThe ref or commit shown, as refnamed it, less any path.pathstringThe directory, from the root; empty for the root. entriesarray of entryEach entry of the directory, in the tree's order. commitsarray of defs#commitViewEach commit an entry names, once. commitCountinteger, optionalHow many commits the ref's history holds, its own among them. Only when pathis the root.Errors
NotFound- GET
dev.knotless.getFile?repo=&ref=&path= a file, highlighted or rendered
Lexicon
One file at a revision: an image, a binary, too large to show, or text, with the ranges its highlighting paints. The bytes themselves are at the repository's raw path. No authentication needed; asked as an account, with a service token or the pages' session cookie, a private repository it is a member of is read too, and the answer is that account's alone.
Parameters
repostring (at-uri)The repository's dev.knotless.repo record. Its authority may be a handle; a name the repository had before, or an owner it moved from with its agreement, finds it under the name it has now. refstring, at most 1024 bytes, optionalA branch, a tag or a commit; the default branch if absent. It may run on into the path, as a web address names them: the longest branch or tag that starts it is the ref, and what follows goes before path.pathstring, at most 4096 bytes, optionalA path in the tree, from its root. Output
refstringThe ref or commit shown, as refnamed it, less any path.pathstringThe file, from the root. kindstring,image,binary,large,textHow it is shown: only textcomes with its text.sizeintegerIn bytes. linesintegertextstring, optionalrangesstring, optionalThe ranges its highlighting paints, encoded as the pages read them. htmlstring, optionalA Markdown file made into HTML, as a README is. Errors
NotFound- GET
dev.knotless.getBlame?repo=&ref=&path= who last changed each line of a text file, as
git blamefinds it: its text, highlighted; its lines in runs (hunks), each with its commit and, where the commit’s parent had the file,previous, that parent and the file’s path there; and each commit once. 400 for a binary or a file over 256 KiBLexicon
Who last changed each line of a text file at a revision, as git blame finds it: the file's text, the ranges its highlighting paints, its lines in runs from the same commit, and each of those commits once. A binary file, or one over 256 KiB, is refused. No authentication needed; asked as an account, with a service token or the pages' session cookie, a private repository it is a member of is read too, and the answer is that account's alone.
Parameters
repostring (at-uri)The repository's dev.knotless.repo record, as dev.knotless.getFile takes it. refstring, at most 1024 bytes, optionalA branch, a tag or a commit, which may run on into the path, as dev.knotless.getFile takes it; the default branch if absent. pathstring, at most 4096 bytes, optionalA file in the tree, from its root. Output
refstringThe ref or commit shown, as refnamed it, less any path.pathstringThe file, from the root. linesintegerHow many lines the text has. textstringThe file's text. rangesstringThe ranges its highlighting paints, encoded as the pages read them, as dev.knotless.getFile has them. hunksarray of hunkIts lines in runs, each last changed by one commit, from the first line on. commitsarray of defs#commitViewEach commit a run names, once. Errors
NotFound,InvalidRequest- GET
dev.knotless.listCommits?repo=&ref=&path=&limit=&cursor=&subjectOnly= history, 50 commits a page by default, with the newest checks of each; with
subjectOnly, each message only to its first lineLexicon
The commits reachable from a revision that changed a path, newest first, a page at a time: follow
cursoruntil it is absent. A repository never pushed has none at its default branch. No authentication needed; asked as an account, with a service token or the pages' session cookie, a private repository it is a member of is read too, and the answer is that account's alone.Parameters
repostring (at-uri)The repository's dev.knotless.repo record. Its authority may be a handle; a name the repository had before, or an owner it moved from with its agreement, finds it under the name it has now. refstring, at most 1024 bytes, optionalA branch, a tag or a commit; the default branch if absent. It may run on into the path, as a web address names them: the longest branch or tag that starts it is the ref, and what follows goes before path.pathstring, at most 4096 bytes, optionalA path in the tree, from its root. limitinteger, 1 to 100, default 50, optionalcursorstring, at most 32 bytes, optionalHow many commits the pages before held. subjectOnlyboolean, default false, optionalEach commit's message cut to its first line, its subject, as a list of commits shows it: far less to send when squashed pulls carry their bodies. Output
refstringThe ref or commit shown, as refnamed it, less any path.pathstringThe path, from the root; empty for the whole tree. commitsarray of defs#commitViewchecksarray of defs#checkView, optionalThe newest run of each workflow at each of commits, by the runners the repository names; each says its commit.cursorstring, optionalAbsent on the last page. Errors
NotFound- GET
dev.knotless.getCommit?repo=&sha= a commit and its diff
Lexicon
A commit, and what it changed, file by file, as
git showhas it. No authentication needed; asked as an account, with a service token or the pages' session cookie, a private repository it is a member of is read too, and the answer is that account's alone.Parameters
repostring (at-uri)The repository's dev.knotless.repo record. Its authority may be a handle; a name the repository had before, or an owner it moved from with its agreement, finds it under the name it has now. shastring, at most 1024 bytesThe commit, by object name or anything else git takes. Output
commitdefs#commitViewfilesarray of defs#fileDiffViewErrors
NotFound- GET
dev.knotless.listBranches?repo=&limit=&cursor=&commits= the branches, the default first, 100 a page by default; with
commits, each with its tip and that commit’s newest checksLexicon
A repository's branches, the default first and the rest by name, a page at a time: follow
cursoruntil it is absent. No authentication needed; asked as an account, with a service token or the pages' session cookie, a private repository it is a member of is read too, and the answer is that account's alone.Parameters
repostring (at-uri)The repository's dev.knotless.repo record. Its authority may be a handle; a name the repository had before, or an owner it moved from with its agreement, finds it under the name it has now. limitinteger, 1 to 1000, default 100, optionalcursorstring, at most 32 bytes, optionalHow many branches the pages before held. commitsboolean, default false, optionalEach branch's tip commit and its checks too, as a page of branches shows them, rather than its name alone, as a menu of them does. Output
branchesarray of branchViewcursorstring, optionalAbsent on the last page. Errors
NotFound- GET
dev.knotless.listTags?repo=&limit=&cursor=&commits= the tags that name a commit, the newest commit first, 100 a page by default; with
commits, each with its commit and that commit’s newest checksLexicon
A repository's tags that name a commit, the newest commit first and the tags of one commit by name, a page at a time: follow
cursoruntil it is absent. No authentication needed; asked as an account, with a service token or the pages' session cookie, a private repository it is a member of is read too, and the answer is that account's alone.Parameters
repostring (at-uri)The repository's dev.knotless.repo record. Its authority may be a handle; a name the repository had before, or an owner it moved from with its agreement, finds it under the name it has now. limitinteger, 1 to 1000, default 100, optionalcursorstring, at most 32 bytes, optionalHow many tags the pages before held. commitsboolean, default false, optionalEach tag's commit and its checks too, as a page of tags shows them, rather than its name alone. Output
tagsarray of tagViewcursorstring, optionalAbsent on the last page. Errors
NotFound- GET
dev.knotless.listItems?repo=&kind=&state=&q=&limit=&cursor= the open or closed pulls or issues that the filter
qmatches (collaboration.md),review-requested:@methe asker, each withdraftfor a draft pull and thereviewersit asks, 50 a page by default, how many of each, and the repository’s labels and the items’ authors, for a filter to nameLexicon
A repository's pulls or its issues, open or closed, newest first, a page at a time: follow
cursoruntil it is absent.qfilters them as GitHub's search does. No authentication needed; asked as an account, with a service token or the pages' session cookie, a private repository it is a member of is read too, and the answer is that account's alone.Parameters
repostring (at-uri)The repository's dev.knotless.repo record. Its authority may be a handle; a name the repository had before, or an owner it moved from with its agreement, finds it under the name it has now. kindstring, at most 16 bytes,pull,issuestatestring, at most 16 bytes,open,closed, optionalOpen ones, or those closed and merged; open if absent. qstring, at most 1024 bytes, optionalA filter, in words: label:<name>(each must be on it),-label:<name>,no:label,author:<handle or did>(any of them),state:open|closed|merged(overstate),is:pull|issue, and words its title or body holds, ignoring case. Double quotes keep a value or words together; a qualifier it does not know is a word. The counts are of those it matches.limitinteger, 1 to 100, default 50, optionalcursorstring, at most 32 bytes, optionalHow many the pages before held. Output
itemsarray of defs#itemViewopenCountintegerHow many of its kind are open, of those qmatches.closedCountintegerHow many of its kind are closed or merged, of those qmatches.cursorstring, optionalAbsent on the last page. labelsarray of repo#label, optionalThe labels the repository defines, in its order, for a filter to name. Absent when it defines none. authorsarray of defs#actorView, optionalWho opened pulls or issues of its kind, in any state, the most recent first, for a filter to name. Absent when there are none. Errors
NotFound- GET
dev.knotless.getThread?repo=&uri=&filesOnly=&firstRows= a pull or an issue and everything said on it, each comment with its
uri, Markdownbodyand, for a review, thecommitit looked at; on a pull’s lines, itsline(path,side,line,startLine, thelinesit names as they were, at most 20, andoutdatedonce that side of the file has changed), thereviewit went with andresolved; an answer’sreply, the comment starting its thread; for a pull,reviews, where each reviewer stands (outdatedonce the open pull has moved past the head it looked at,maintainerfor the owner or a collaborator), and inchangestheheadit is at; for a merged pull,revertedBy({uri, title}), the revert whose merge undid it; for an issue,mentionedBy, the pulls on the repository whose title or body names it, oldest first, each itsitemandcloseswhere it names it after a closing keyword; in eachhtml, a bareat://URI of a pull or issue on the repository, or a link to one, as a link to its page, a bare one showing its title; for a pull of a public repository, the minutes each runner counts for its owner (dev.knotless.minutes); with an index,reactionson the item and on each comment, by content in GitHub’s order, each naming the asker’s ownreactionwhen it has an asker; and with an asker, what it may do (viewer:setState,hide,revertfor a merged pull, and thelabelsit may set); withfilesOnly, a pull’s changed files without their rows, and withfirstRowstoo, the rows of the first files, up to 10 files or 1500 lines, passing over any that changes more than 400Lexicon
A pull or an issue on a repository: what it says, what was said on it, and for a pull what it brings in, how its checks stand and where it is in the merge queue. No authentication; asked as an account, with a service token or the pages' session cookie, the answer says what that account may do with it, and which reactions are its own. A private repository is read for its members asking so.
Parameters
repostring (at-uri)The repository's dev.knotless.repo record. Its authority may be a handle; a name the repository had before, or an owner it moved from with its agreement, finds it under the name it has now. uristring (at-uri)The dev.knotless.pull or dev.knotless.issue record. filesOnlyboolean, default false, optionalA pull's changed files without their rows: what each is, how many lines it adds and removes, and a note where its rows would not be shown. dev.knotless.getPullDiff gives the rows of the files a reader comes to. firstRowsboolean, default false, optionalWith filesOnly, the rows of the first files too, as a page draws them at once: from the first, those whose rows would be shown, up to 10 files and 1500 changed lines, passing over any that changes more than 400. The rest come without, for dev.knotless.getPullDiff. Output
itemdefs#itemViewbodystring, optionalWhat it says, as its author wrote it, in Markdown. htmlstring, optionalIts body, made into HTML as a comment's is. basestring, optionalA pull's target branch. headdefs#pullHeadView, optionalWhat a pull brings in. mergedstring, optionalFor a merged pull, the commit it merged as. commentsarray of defs#commentViewOldest first. reviewsarray of defs#reviewView, optionalFor a pull, where each reviewer stands, in the order they last reviewed. Absent when no one has. closesarray of defs#itemViewThe issues on the repository a pull's title or body names to close as it merges into the default branch. closedBydefs#itemViewBasic, optionalFor a closed issue, the pull whose merge closed it. mentionedByarray of mention, optionalFor an issue, the pulls on the repository whose title or body names it, oldest first, whatever their state. Absent when none does. revertedBydefs#itemViewBasic, optionalFor a merged pull, the revert whose merge undid it: a pull naming it in its reverts, merged too. The newest, if it was undone more than once.queuedefs#queueStateView, optionalWhere an open pull stands in its branch's merge queue, if it was queued or ejected from it. checksarray of defs#checkViewFor a pull, the newest run of each workflow at its head: by the target's runners for the pull, and by those of the repository it comes from for its branch. For a pull the merge queue landed, the runs it landed on: those on its base at its batch's tip. minutesarray of defs#minutesView, optionalFor a pull, the minutes each runner of the target counts for the target's owner, where it has counted any; none for a private repository. changesdefs#changesView, optionalWhat a pull brings in; absent for an issue, or a pull whose branch is gone. cardstringThe path of the picture its page unfurls into. reactionsarray of defs#reactionCountView, optionalHow accounts reacted to the pull or issue itself, by content, as dev.knotless.getReactions counts them. Absent when none did. viewerviewerState, optionalWhat the account asking may do with it; absent when no one asks. Errors
NotFound- GET
dev.knotless.getPullDiff?repo=&uri=&paths= the files of a pull’s diff that
pathsnames (repeated, 1 to 50), rows and all: what the pull page fetches as the reader comes to each fileLexicon
The files of a pull's diff that
pathsnames, rows and all, as dev.knotless.getThread shows them, for a page that listed them with itsfilesOnly. No authentication; responses are not personalized.Parameters
repostring (at-uri)The repository's dev.knotless.repo record. Its authority may be a handle; a name the repository had before, or an owner it moved from with its agreement, finds it under the name it has now. uristring (at-uri)The dev.knotless.pull record. pathsarray of string, at most 4096 bytes, at most 50The files, by their paths after the change, or before it for one deleted; a renamed file is found as such only when both its paths are named. Output
filesarray of defs#fileDiffViewThose of the files named that the pull changes, in the diff's order. Errors
NotFound- GET
dev.knotless.getTemplate?repo=&kind=&base= the template a new pull (at
base) or issue is asked to follow,.knotless/<kind>.md, asknotless templatereads it;{}with none, 400 for akindthat is neitherLexicon
The template a repository asks its pulls or issues to be written from, as
knotless templatereads it:.knotless/pull.mdor.knotless/issue.md, at a pull's base branch or else the default branch. Opening one whose body has none of the template's sections is refused. No authentication; responses are not personalized.Parameters
repostring (at-uri)The repository's dev.knotless.repo record. Its authority may be a handle. kindstring, at most 64 bytes,pull,issueWhat is to be opened. basestring, at most 1024 bytes, optionalFor a pull, the branch it is to merge into: the template there. Output
templatestring, optionalThe template, in Markdown; absent when the repository has none. Errors
NotFound- GET
dev.knotless.getCompare?repo=&base=&head=&branch= the repositories a pull can come from (
heads: this one, then its forks), and withbranchwhat it brings in fromhead(this repository if not given) intobase(the default branch if not given), as a pull’schanges: 404 for a branch on neitherLexicon
The repositories a pull into a repository can come from, itself and its forks, and, given a branch, what that branch has since it left the base, as a pull from it would show. No authentication; responses are not personalized.
Parameters
repostring (at-uri)The repository to merge into: its dev.knotless.repo record. Its authority may be a handle. basestring, at most 1024 bytes, optionalThe branch to merge into; the default branch if not given. headstring (at-uri), optionalThe repository the branch is on: this one, if not given, or a fork of it. branchstring, at most 1024 bytes, optionalThe branch to bring in. Without one, only the repositories are answered. Output
headsarray of defs#repoViewBasicThe repositories a pull can come from: this one first, then its forks the index knows, oldest first. changesdefs#changesView, optionalWhat the branch brings in, given one. Errors
NotFound- GET
dev.knotless.getReactions?repo=&uri= the reactions to a pull or an issue and to each comment on it, as
{subjects: [{subject, reactions: [{content, count, reaction}]}]}, leaving out subjects none reacted to: 404 with no indexLexicon
How many accounts reacted to a pull or an issue, and to each comment on it, with each content, as the appview has indexed their dev.knotless.reaction records; for a private repository, as its space has them, to its members. Accounts taken down, or that the repository's owner blocked, are left out, as are comments the owner hid. Personalized when an account asks, by a service token or the pages' session cookie: its own reactions are named.
Parameters
repostring (at-uri)The repository's dev.knotless.repo record, as dev.knotless.getThread takes it. uristring (at-uri)The dev.knotless.pull or dev.knotless.issue record. Output
subjectsarray of defs#reactionStateThe pull or issue, then each comment on it oldest first: those any account reacted to. Errors
NotFound- GET
dev.knotless.getQueue?repo= the merge queues, what they ejected, and what they landed last
Lexicon
What is queued on each branch of a repository, in the order it lands, the batches being checked, the open pulls the queue turned away and why, and the batches it landed last, as the runners the repository names report them. No authentication; responses are not personalized.
Parameters
repostring (at-uri)The repository's dev.knotless.repo record. Its authority may be a handle; a name the repository had before, or an owner it moved from with its agreement, finds it under the name it has now. Output
branchesarray of branchViewEach branch with pulls queued, by name. ejectedarray of ejectedViewThe open pulls the queue turned away, newest first. landedarray of landedViewThe last few batches landed, newest first. Errors
NotFound- GET
dev.knotless.listRuns?repo=&workflow=&limit=&cursor= the runs of the repository’s workflows, or of one, newest first, 30 a page by default and at most 100, each with the ref, pull or batch it ran on; and the workflows on the default branch, those that run only when asked among them
Lexicon
The runs of a public repository's workflows that the runners its record names reported, newest first, of every workflow or of one: each as its check, with what it ran on. And the workflows on its default branch, those that run only when asked for by name among them. A page at a time: follow
cursoruntil it is absent. No authentication; responses are not personalized.Parameters
repostring (at-uri)The repository's dev.knotless.repo record. Its authority may be a handle; a name the repository had before, or an owner it moved from with its agreement, finds it under the name it has now. workflowstring, at most 64 bytes, optionalOnly the runs of this workflow, by name. limitinteger, 1 to 100, default 30, optionalcursorstring, at most 2048 bytes, optionalThe cursorof the page before.Output
runsarray of runViewcursorstring, at most 2048 bytes, optionalAbsent on the last page. workflowsarray of workflowViewThe workflows in .knotless/workflows on the default branch, by name. minutesarray of defs#minutesView, optionalThe minutes each of the repository's runners counts for its owner, where it has counted any; none for a private repository. - GET
dev.knotless.getCheck?repo=&uri= a check, its steps and the end of its log
Lexicon
A check of one of a repository's workflows, as its runner reported it: each step, and the end of what they printed. Only a check by a runner the repository's record names is found. No authentication; responses are not personalized.
Parameters
repostring (at-uri)The repository's dev.knotless.repo record. Its authority may be a handle; a name the repository had before, or an owner it moved from with its agreement, finds it under the name it has now. uristring (at-uri)The dev.knotless.check record, by a runner the repository names. Output
checkdefs#checkViewrefstringThe ref it ran at, in full, such as refs/heads/main. pullstring (at-uri), optionalThe dev.knotless.pull it ran for, if it ran for one. runnerdefs#actorViewThe account that ran it. stepsarray of check#stepIn the order they ran. logstring, optionalThe end of what its steps printed, each step's after a line ==> <name>.Errors
InvalidRequest,NotFound- GET
dev.knotless.getCheckImage?repo=&uri=&cid= a picture a check names
Lexicon
A picture a check made, from its runner's PDS: only one the check names, of a repository by a runner it names. Served so that a browser never runs it as a page, and the same for good. No authentication; responses are not personalized.
Parameters
repostring (at-uri)The repository's dev.knotless.repo record. Its authority may be a handle; a name the repository had before, or an owner it moved from with its agreement, finds it under the name it has now. uristring (at-uri)The dev.knotless.check record, by a runner the repository names. cidstring (cid)The picture's blob. Errors
InvalidRequest,NotFound- GET
dev.knotless.getAvatar?actor=&cid= a
dev.knotless.profilepictureLexicon
The picture an account's dev.knotless.profile names, from its PDS: only that one. Served so that a browser never runs it as a page, and the same for good. The views give it as
avatar. No authentication; responses are not personalized.Parameters
actorstring (did)The account. cidstring (cid)The picture's blob. Errors
NotFound- GET
dev.knotless.getAttachment?repo=&uri=&cid= a blob a pull, issue or comment attaches, such as a picture its body shows, from its author’s PDS or their repo in a private repository’s space; 404 for one its record does not attach
Lexicon
A blob a dev.knotless.pull, dev.knotless.issue or dev.knotless.comment record attaches, of any kind in its
attachments, from its author's PDS, or for a private repository from their repo in its space: only one the record names. Served so that a browser never runs it as a page, with the type its record gives when that is one shown safely, and the same for good. dev.knotless.getThread'shtmlshows its pictures from here. No authentication beyond what reading the repository takes; responses are not personalized.Parameters
repostring (at-uri)The repository's dev.knotless.repo record, as dev.knotless.getRepo takes it. uristring (at-uri)The pull, issue or comment. cidstring (cid)The attached blob. Errors
NotFound- GET
dev.knotless.getViewer who is asking (
actor), the other accounts the browser is logged in to (accounts), whether this server offers a login (login, only with--public-url),admin: truefor an operator,delegated: truewhen its gateway writes for the asker,organization: truewhen the asker is an organization,reauth: truewhen their login to the pages asked for less than the pages ask for now,redelegate: truewhen the gateway writes for them under a grant that asked for less than it does now, andspaces, whether the asker’s PDS has spaces for private repositories, once this server has listed theirs (as they log in)Lexicon
The account asking: by the session cookie the pages' login sets, or a service token. Nothing, for no one.
Output
actordefs#actorView, optionalThe account asking; absent when no one is. loginboolean, optionalWhether this appview's pages can log people in: it is an OAuth client, with a public URL. accountsarray of defs#actorView, optionalThe other accounts this browser is logged in to, which it can switch to without logging in again; absent when none. adminboolean, optionalWhether the account asking is one of this appview's operators, who moderate it and see how it is used; absent when not. delegatedboolean, optionalWhether this appview's gateway writes for the account asking, which delegated writes to it: collaborators push to its repositories through it, and runners land their merge queues. Absent when not. organizationboolean, optionalWhether the account asking is an organization: it keeps a public dev.knotless.org record. Absent when not. spacesboolean, optionalWhether the account asking has a PDS with spaces, which private repositories live in; absent when no one asks, or this appview does not know yet. reauthboolean, optionalWhether the account asking logged in to these pages asking for less than they ask for now, as before they came to do more: logging in again grants the rest. Absent when not. redelegateboolean, optionalWhether this appview's gateway writes for the account asking under a grant asking for less than it asks for now: delegating writes to it again grants the rest. Absent when not. - GET
dev.knotless.getFollowers?actor=&limit=&cursor= the accounts following an account, the latest first, 50 a page by default: 404 with no index,
ProfileNotFoundfor an unknown accountLexicon
The accounts whose dev.knotless.follow records name the account, as the appview has indexed them, the latest followed first. An account taken down is left out. No authentication; responses are not personalized.
Parameters
actorstring (at-identifier)The account, by DID or handle. limitinteger, 1 to 100, default 50, optionalcursorstring, at most 512 bytes, optionalWhere the page before ended. Output
actorsarray of defs#actorViewcursorstring, optionalAbsent on the last page. Errors
ProfileNotFound- GET
dev.knotless.getFollows?actor=&limit=&cursor= the accounts an account follows, the same way
Lexicon
The accounts the account's dev.knotless.follow records name, as the appview has indexed them, the latest followed first. An account taken down is left out. No authentication; responses are not personalized.
Parameters
actorstring (at-identifier)The account, by DID or handle. limitinteger, 1 to 100, default 50, optionalcursorstring, at most 512 bytes, optionalWhere the page before ended. Output
actorsarray of defs#actorViewcursorstring, optionalAbsent on the last page. Errors
ProfileNotFound- GET
dev.knotless.listThemes?actor=&q=&uris=&limit=&cursor= dev.knotless.themerecords, everyone’s oractor’s, the latest first, as{themes, cursor}; withq, everyone’s whose name starts with it in any case, by name, with no cursor; withuris, repeated, 50 at most, those it names that there are, in its order, with no cursor and the rest passed over: each{uri, creator, name, palette, createdAt}, its palette only the colours that are#rrggbb, and one with no name or no colour it must give left out, as is an account taken down.ProfileNotFoundfor no such actor, 400 for over 50uris, 404 with no indexLexicon
The dev.knotless.theme records the appview has indexed, the latest made first: everyone's, or one account's; or by the start of their name. An account taken down is left out. No authentication; responses are not personalized.
Parameters
actorstring (at-identifier), optionalOnly this account's themes, by DID or handle. urisarray of string (at-uri), at most 50, optionalOnly these themes, by their dev.knotless.theme URIs, as an account's added themes name them: those the appview has, in the order named, with no cursor; the other parameters are passed over. qstring, at most 64 bytes, optionalOnly themes whose name starts with this, in any case: those found by name, in its order, with no cursor. limitinteger, 1 to 100, default 50, optionalcursorstring, at most 512 bytes, optionalWhere the page before ended. Output
themesarray of defs#themeViewcursorstring, optionalAbsent on the last page. Errors
ProfileNotFound,NotFound- GET
dev.knotless.listSteps?repo=&q=&limit=&cursor= the shared steps the index has found (serve.md), everyone’s or one repository’s (
repo, itsdev.knotless.repoURI, by handle or DID), by name, 50 a page by default, those whose name starts withqif given, as{steps, cursor}: each at its latest version, the highest that is not a pre-release, as{uri, name, repo, description, version, secretCount, useCount, createdAt},useCounthow many public repositories pin it in the.knotless/uses.lockof their default branch. One taken down, or of an account or repository taken down, is left out. 400 for arepothat is not a repository’s URI, 404 for no such repository or with no indexLexicon
The shared steps the appview has indexed, from dev.knotless.release records of packages named
@step/<name>whose one file is a step file, each at its latest version, in the order of their names: everyone's, one repository's, or those whose name starts withq. A step of an account, repository or release taken down is left out. No authentication; responses are not personalized.Parameters
repostring (at-uri), optionalOnly the steps of this repository, by its dev.knotless.repo record. Its authority may be a handle. qstring, at most 64 bytes, optionalOnly steps whose name starts with this. limitinteger, 1 to 100, default 50, optionalcursorstring, at most 512 bytes, optionalWhere the page before ended. Output
stepsarray of defs#stepViewcursorstring, optionalAbsent on the last page. Errors
NotFound- GET
dev.knotless.getStep?repo=&name= one of those, as
listStepsgives it, with its latest version’scommit, its file’spath, theusesthat takes its major version (<handle>/<repo>/<name>@1, or@0.3before 1.0.0), itsinputs, each{name, description, required, default}, the commands itneedsand thesecretsit asks for, each{name}, itsversions, each{uri, version, commit, createdAt}, the highest first, andusedBy, at most 30 of the repositories pinning it. 404 for no such repository or step, or with no indexLexicon
A shared step at its latest version, from the dev.knotless.release records of its package,
@step/<name>: what it does, takes and asks for, each version published, and the public repositories whose default branch pins it in.knotless/uses.lock. No authentication; responses are not personalized.Parameters
repostring (at-uri)The step's repository, by its dev.knotless.repo record. Its authority may be a handle. namestring, at most 64 bytesThe step's name. Output
defs#stepViewDetailedErrors
NotFound- GET
dev.knotless.listNotifications?limit=&cursor=&kind= what others did that concerns the asker, the latest first, a pull or an issue opened on any repository it looks after (its own, those naming it a collaborator, its organizations’), 50 a page by default, each
isReadbyseenAt, and a review’sverdict;reasonmentionfor a pull, issue or comment whose facets name the asker, dated by the edit that first named the asker, and each record once, for its other reason when it has one the asker hears of, then withmentioned: true;reasonSubjectwhat each is about, the pull or issue a comment is on, else the pull or issue itself, or for a star the repository’s record;lineComments, how many line comments a review was sent with;review-requestedfor a pull, not a draft, that asks the asker to review it;reasoninvitefor an organization’s invitation the asker has yet to answer, while it stands, with the organization asauthor, itsdev.knotless.orgMemberrecord of the asker asuri, theroleit offers and norepo;reasonchecks-failedfor the asker’s pull whose checks failed at the latest run of each workflow, one for each head and base, with a runner the repository names asauthor, a failed check asuri, the pull asreasonSubjectandchecks, each{workflow, status, check};reasonejectedfor the asker’s pull the merge queue took out, with its runner’sdev.knotless.queuerecord asuriand the checks that failed aschecks. A pull, an issue, a comment, a mention in one, or a review request hastext, the start of what it says as Markdown, at most 1,000 characters; a review request hasreviewed: trueonce the asker has approved the pull or asked for changes since it was asked; each about a pull or an issue hassubjectState,open,closedormerged, as its page says, andchecks-failedandejectedare left out once their pull has merged or closed.kindnarrows them to one ofmentions(with comments heard of for another reason that name the asker),reviews(review requests, and comments with averdict),comments(those without),pulls-and-issues,checks(checks-failedandejected) orstars-and-follows; an unknown kind answers none: 401AuthenticationRequiredwith no asker, 404 with no indexLexicon
What others did that concerns the account asking, as the appview has indexed it, the latest first: stars of its repositories, follows of it, invitations to join an organization it has yet to take up, while they stand, pulls and issues opened on the repositories it looks after (its own, those whose records name it a collaborator, and those of the organizations it joined), comments and reviews on its pulls and issues, and on those it commented on, after it did, pulls, issues and comments that mention it, and, for its own pulls, checks that failed and the merge queue turning them away. One record is one notification: a comment that mentions the author of what it is on is heard of as a comment, marked as mentioning them, and a review's line comments are heard of with it, counted. Leaves out its own doings, and accounts it blocked or that are taken down. Those of a private repository come from the appview's own index of its space, where its owner lets it keep one, while the account can read it: a mention of someone who cannot is never heard of. Requires authentication: a service token, or the session cookie the pages' login sets.
Parameters
limitinteger, 1 to 100, default 50, optionalcursorstring, at most 512 bytes, optionalWhere the page before ended. kindstring,mentions,reviews,comments,pulls-and-issues,checks,stars-and-follows, optionalOnly those of this kind: mentions, and comments heard of for another reason that mention the asker too;reviewsasked of it, and comments that review its pulls;commentsthat do not review;pulls-and-issuesopened;checksthat failed and the merge queue turning a pull away;stars-and-follows. A kind the appview does not know answers nothing.Output
notificationsarray of notificationcursorstring, optionalAbsent on the last page. seenAtstring (datetime), optionalWhen the account last said it had seen its notifications, if ever. Errors
AuthenticationRequired- GET
dev.knotless.getUnreadCount how many of those are new, counting at most 100
Lexicon
How many of the notifications dev.knotless.listNotifications gives the asking account are from after it last saw them, counting at most 100. Requires authentication: a service token, or the session cookie the pages' login sets.
Output
countinteger, 0 to 100Errors
AuthenticationRequired- GET
dev.knotless.getPreferences the asker’s preferences, kept by the appview, as
{preferences}, each by its$type:#appearancePref,{theme, palette, source, night}, once it chose one,nightthe same three for while the system is dark, if it chose two;#themesPref,{themes}, the themes people made that it added, each{uri, name, creator, palette, addedAt}, once it added one; and#notificationPrefalways, which it hears of:reasons, eachlistNotificationsreason (from its lexicon’sknownValues, so a new one is listed) withenabled, andfollowedOnly, whether only of what accounts it follows did. Until it says, all, from anyone.listNotificationsandgetUnreadCountleave out the rest: 401 with no asker, 404 with no indexLexicon
The asking account's preferences, as the appview keeps them, apart from any record: one of each kind, of those it has set, and always its notifications, which dev.knotless.listNotifications and getUnreadCount follow. A kind it has not set a reader takes as its default. The pages' login copies what they act on into the browser. Requires authentication: a service token, or the session cookie the pages' login sets.
Output
preferencesErrors
AuthenticationRequired,NotFound- GET
dev.knotless.getOwnProfile the asker’s
dev.knotless.profilealone, as its settings show it (displayName,description,pronouns,links,avatar’s address,bot), with nothing of its Bluesky profile: 401 with no askerLexicon
The asking account's dev.knotless.profile record, as its settings show it: only what it set on knotless, with nothing of its Bluesky profile, which stands in for each field left out (dev.knotless.getProfile shows the two together). Requires authentication: a service token, or the session cookie the pages' login sets.
Output
ownProfileErrors
AuthenticationRequired- GET
dev.knotless.getAccountData what knotless data the account logged in to the pages holds, read from its PDS:
{did, collections, repos, spaces}, eachdev.knotless.*collection it writes in as{collection, count}, its repositories as{name, private, collaborators, members?}(members, for a private one, the accounts that read it but its runners), and the spaces of its private repositories; the pages’ cookie only, 401 otherwiseLexicon
Counts the records the asking account holds in each dev.knotless.* collection, read from its PDS rather than the index, and names its repositories, public and private, with how many other accounts push to or read each: what dev.knotless.deleteAccountData would delete, as
knotless delete-datalists it. A private repository's records are in its space, counted as one space. Only the account itself, logged in to this appview's web pages, can ask: the appview reads its spaces with the session it keeps for it, which the pages' session cookie names.Output
didstring (did)The account. collectionsarray of collectionCountEach dev.knotless.* collection the account holds records in, in the order of their names. reposarray of repoDataThe account's repositories, public ones first, each by name. spacesintegerThe spaces the account owns for private repositories: one for each, and one for each name a rename of one left. Errors
AuthenticationRequired- GET
dev.knotless.getEmails {count}, how many addresses the asker’sdev.knotless.emailsclaims: only hashes are kept, so they cannot be listed. 401 with no askerLexicon
How many addresses the asking account's dev.knotless.emails record claims. The record keeps each only as a hash, salted with the account's DID, so no one, the appview included, can list them. Requires authentication: a service token, or the session cookie the pages' login sets.
Output
countintegerHow many addresses it claims. Errors
AuthenticationRequired- GET
dev.knotless.listBlocks the asker’s blocks, the latest first, from its own repo: each
uri,subject(an actor),repowhen it is from one repository only, andcreatedAt. Those naming a private repository live in its space and are not listed. 401 with no askerLexicon
The accounts the asking account keeps out of its repositories, from the dev.knotless.block records in its own repo, so a block lists as soon as it is made: the latest first, each from all its repositories or from one. Blocks naming a private repository live in its space, and are not listed. Requires authentication: a service token, or the session cookie the pages' login sets.
Output
blocksarray of blockErrors
AuthenticationRequired- GET
dev.knotless.getRepoSettings?repo= what the owner sets: description, links, default branch, protected branches, labels, collaborators, triagers, gateway, an offer to move it (
transfer),storage, asknotless storagecounts it, the gateway the owner delegated to from these pages (delegated), and for a private one, to its owner alone, the members who read but do not push (readers)Lexicon
A repository's settings, read from its dev.knotless.repo record as its PDS has it now, for the pages' settings to show and change. All of it is public, as the record is.
Parameters
repostring (at-uri)The repository's dev.knotless.repo record, as dev.knotless.getRepo takes it. Output
uristring (at-uri)Its dev.knotless.repo record, under the name it has now. descriptionstring, optionalWhat the repository says it is. linksarray of defs#link, optionalThe links its pages show, in order, as its record names them. topicsarray of repo#topic, optionalWhat it is about, in order, as its record names them. defaultBranchstring, optionalThe branch its record names for HEAD, without refs/heads/. rulesarray of repo#ruleIts protected branches. labelsarray of repo#labelThe labels its pulls and issues may carry. collaboratorsarray of defs#actorViewThe accounts that may push to it. triagersarray of triagerViewThe accounts whose labels of some scopes count. gatewaystring (did), optionalThe gateway the owner delegated writes to, through which collaborators push: absent until the owner delegates. transferdefs#repoViewBasic, optionalWhere the owner has offered to move it, if they have: the account, and the name it would have there. storagestorageView, optionalWhat it takes up on its owner's PDS; absent if that could not be read. indexedboolean, optionalDeprecated, and no longer set: knotless.dev indexes every private repository its members read there. Was, for a private repository, whether its owner let this appview keep an index of it. delegatedstring (did), optionalThe gateway its owner delegated writes to from this appview's pages (dev.knotless.delegateWrites), which sharing it for writing names as its gateway; absent until they have. readersarray of defs#actorView, optionalFor a private repository, asked by its owner, the members who read it but do not push to it, its runners among them; absent for a public one, which anyone reads, and empty to anyone else. Errors
NotFound- GET
dev.knotless.getOrg?actor= an organization’s members, as the index has its
dev.knotless.organddev.knotless.orgMemberrecords and its members’dev.knotless.orgAcceptanceones:{org, organization, inside, gateway?, members},gatewaythe one its publicdev.knotless.orgrecord names, each member{actor, role, standing, invitedAt, expiresAt?, joinedAt?}, the first invited first,standingone ofinvited,lapsed,publicandhidden. To anyone, its members in public; to the organization and its members, logged in or with a service token (inside), its invitations and hidden members too, from its space, which this server keeps an index of; to an account it invited, its own entry.organization: false, with no members, for an account that is none;ProfileNotFoundfor an unknown accountLexicon
The accounts an organization (any account keeping a dev.knotless.org record) invited, as the appview has indexed its records. To anyone, those who are members in public: named by a dev.knotless.orgMember record in its public records, taken in, and keeping a public dev.knotless.orgAcceptance record naming it. Optional authentication: to the organization itself and to its members, its invitations and hidden members too, from its space; to an account it invited, its own entry.
Parameters
actorstring (at-identifier)The organization, by DID or handle. Output
orgdefs#actorVieworganizationbooleanWhether the account is an organization: it keeps a public dev.knotless.org record. insidebooleanWhether the asker sees its invitations and hidden members: it is the organization, or one of its members. gatewaystring (did), optionalThe gateway its public dev.knotless.org record names, which members join and leave through, and admins change its members through. membersarray of memberViewThe first invited first: to an asker inside, every account it invited; to anyone else, its members in public. Errors
ProfileNotFound- GET
dev.knotless.getActorStars?actor= the public repositories an account starred, newest first, each with its
language,starCountandforkCount: 404 with no index,ProfileNotFoundfor an unknown accountLexicon
The public repositories an account starred, as the appview has indexed its dev.knotless.star records, the latest starred first. A star naming a repository the appview does not know, or one taken down, is left out. No authentication; responses are not personalized.
Parameters
actorstring (at-identifier)The account, by DID or handle. Output
reposarray of defs#repoViewErrors
ProfileNotFound- GET
dev.knotless.getMergeStatus?repo=&pull= whether an open pull can merge now, as
knotless merge --checksays (below): 401 with no asker, 400 for a pull not open, 404 for noneLexicon
Whether an open pull can merge now, decided as
knotless mergedecides it, without merging: whether its head merges with its target branch as it is now, and, in a repository that names runners, whether each workflow that runs on it passed there. And what the account asking may do to it: merge it, queue it, retarget it or run its checks again. Requires the session cookie the pages' login sets, or a service token from the caller's PDS, audience the appview's DID, for this method.Parameters
repostring (at-uri)The repository's dev.knotless.repo record, as dev.knotless.getRepo takes it. pullstring (at-uri)The dev.knotless.pull record. Output
statestring,clean,conflicting,blocked,draftclean when it can merge now; conflicting when its head does not merge with its base; blocked when its checks have not all passed, or it is not reviewed as its target branch's rule asks; draft, whatever else holds, while its author has not marked it ready. reasonstring, optionalWhy it cannot merge now, in the words a merge would refuse it with; and when the account asking may not merge it, that too. conflictsarray of conflict, optionalThe files that conflict. checksstring,passed,failed,running,pending,noneWhere its checks stand at its head merged with its base: pending for those not run there; none when the repository names no runners, so a merge waits for none, or when it conflicts. methodstring,fastForward,merge,squashHow it would merge: a fast-forward when the base has not moved since its head branched off, else a merge commit; into a branch protected as linear, one squashed commit. deletesstring, optionalIts head branch, which merging deletes unless asked to keep it. basestringThe commit its target branch is at, in hex. headstringThe commit its head branch is at, in hex. maypermissionsqueueBlockedstring,undelegated, optionalWhy the merge queue would not land it yet, when this server knows: undelegated when no runner the repository names may push, so its queue lands through this server's gateway, and the owner has not delegated writes to it. queueOnlyboolean, optionalIts target branch takes pulls only through the merge queue: queue it with dev.knotless.queuePull, as dev.knotless.mergePull refuses it. Absent is false. reviewsstring,passed,blocked,none, optionalWhere its reviews stand against what its target branch's rule asks of them (dev.knotless.repo#reviews): none when the rule asks nothing of them, or when it conflicts. reviewsMissingarray of note, optionalWhat its reviews lack, each as knotless merge --checklists it:reviews 1 of 2,owners of web/: @alice or @bob,changes asked by @carol.reviewsUncountedarray of note, optionalThe approvals that do not count toward its target branch's rule, and why: @dan's approval does not count: only the owner's and collaborators' approvals count, or one of an earlier head when only approvals of its head as it is now count.codeOwnersWarningsarray of note, optionalThe lines of the target branch's CODEOWNERS file that were skipped, and why: one naming a team, say. Errors
NotFound,AuthenticationRequired- GET
dev.knotless.listWorkflows?repo=&ref= the workflows at a ref (the default branch), in file name order, and what runs each there:
push,pull,asked(its owner may ask for it there by name),error; 404 for no such refLexicon
The workflows under .knotless/workflows at a ref of a repository, in file name order, each with what runs it there, as
knotless workflowslists them: which its owner may ask for by name, with variables, through dev.knotless.requestRerun. No authentication; responses are not personalized.Parameters
repostring (at-uri)The repository's dev.knotless.repo record, as dev.knotless.getRepo takes it. refstring, at most 1024 bytes, optionalA branch or a tag, by name or in full; the default branch if none. Output
refstringThe ref, in full. workflowsarray of workflowViewErrors
NotFound- GET
dev.knotless.listRunners?repo= the runners the repository names, as accounts, for a public repository the minutes each counts for its owner (
dev.knotless.minutes), and the key each publishes to seal secrets to (dev.knotless.runnerKey), askeys,{runner, key}Lexicon
The accounts a repository names in its record as its runners, as
knotless runnerslists them: those whose checks of it are shown, and who land its merge queue. No authentication; responses are not personalized.Parameters
repostring (at-uri)The repository's dev.knotless.repo record, as dev.knotless.getRepo takes it. Output
runnersErrors
NotFound
getRepo’s answer has starCount, how many accounts starred it, when the
server keeps an index and the repository is public, and viewer.star, the
asker’s star, when it has an asker. With an index, a public repository
that has published releases has releases: how many (count), the
release to show under its name (release: the latest of the package
named as the repository, else the newest that is its package’s latest,
or the newest of all when each is a pre-release), and
its packages, as listReleases gives them. getProfile’s has followersCount
and followsCount when the server keeps an index, and
viewer.following, the asker’s follow, and viewer.blocking, its block
from all its repositories, when it has an asker; and orgs, the
organizations it is a member of in public, by actor, and to the account
itself, asking, its hidden memberships too, when it has any; and
organization: true when it is an organization, which its page then
shows the members of.
Logging in and stars
Section titled “Logging in and stars”The pages log in and star through these, which exist only on a server with
--public-url, else 404. Each POST but the callback is refused 403 not from this site unless its Origin is the site’s own.
- GET
/oauth/web-client-metadata.json the pages’ OAuth client metadata
- POST
/oauth/login {handle, back}:{url}, the PDS’s page to approve on, and aknotless_logincookie for 15 minutes; 400No account answers to <handle>., 403<handle> is taken down here.Or{create: true, back}: the same, the page being the sign-up page of the PDS--sign-up-pdsnames, and the account made there logged in as it comes back- GET
/oauth/web-callback where the PDS sends the browser back: on to
back(a path on the site, else/), settingknotless_sessionfor 90 days if approved, and keeping the account it replaces, if any, inknotless_accounts; 400 for a login this browser did not start- POST
/oauth/switch {did}: makes that account, oneknotless_accountsholds, the one in use, keeping the one before in its place; 400This browser is not logged in to that account.- POST
/oauth/logout {}: revokes the session, refuses every earlier cookie for the account, and goes on as the other account used last, or clears the cookie if there is none- POST
/xrpc/dev.knotless.starRepo {repo}: stars it as the account logged in, answering{starCount, star}Lexicon
Writes a dev.knotless.star record naming the repository in the account logged in to this appview's web pages, unless one is there already. Requires the session cookie the pages' login sets: the appview writes with the session it keeps for that account. Clients holding their own session write the record themselves.
Input
repostring (at-uri)The repository's dev.knotless.repo record, as dev.knotless.getRepo takes it. Output
defs#starStateErrors
NotFound,AuthenticationRequired- POST
/xrpc/dev.knotless.unstarRepo {repo}: takes the account’s star back, answering{starCount}Lexicon
Deletes every dev.knotless.star record naming the repository in the account logged in to this appview's web pages. Requires the session cookie the pages' login sets.
Input
repostring (at-uri)The repository's dev.knotless.repo record, as dev.knotless.getRepo takes it. Output
defs#starStateErrors
NotFound,AuthenticationRequired- POST
/xrpc/dev.knotless.updateSeen {seenAt}: keeps that the asker saw its notifications up to then, unless it saw later ones already, answering the{seenAt}kept; with a service token too, and with the cookie only from the site; 400 for aseenAtthat is no datetimeLexicon
Keeps, on the appview, when the asking account last saw its notifications: those from then on are unread. A time before the one kept leaves it. Requires authentication: a service token, or the session cookie the pages' login sets, from the pages themselves.
Input
seenAtstring (datetime)The newest notification seen, by its createdAt. Output
seenAtstring (datetime)When it last saw them now, as kept. Errors
AuthenticationRequired- POST
/xrpc/dev.knotless.followActor {subject}, a DID: follows it as the account logged in, answering{followersCount, following}; 400 for the account itself,ProfileNotFoundfor no accountLexicon
Writes a dev.knotless.follow record naming the account in the account logged in to this appview's web pages, unless one is there already. An account cannot follow itself. Requires the session cookie the pages' login sets: the appview writes with the session it keeps for that account. Clients holding their own session write the record themselves.
Input
subjectstring (did)The account to follow. Output
defs#followStateErrors
ProfileNotFound,AuthenticationRequired- POST
/xrpc/dev.knotless.unfollowActor {subject}: takes the account’s follows of it back, answering{followersCount}Lexicon
Deletes every dev.knotless.follow record naming the account from the account logged in to this appview's web pages, if any. Requires the session cookie the pages' login sets: the appview writes with the session it keeps for that account. Clients holding their own session write the record themselves.
Input
subjectstring (did)The account to unfollow. Output
defs#followStateErrors
ProfileNotFound,AuthenticationRequired- POST
/xrpc/dev.knotless.createTheme {name, palette}: writes adev.knotless.themeas the account logged in, and indexes it, answering it aslistThemesdoes; 400 for a name empty or over 64 characters, a scheme notlightordark, a colour not#rrggbb, none ofbackground,text,primaryandaccent, or a text that would not read at 4.5 to 1 on what it is drawn on, each named, astextMuted on background is 1.2 to 1Lexicon
Writes a dev.knotless.theme record in the account logged in to this appview's web pages, for anyone to choose. Requires the session cookie the pages' login sets: the appview writes with the session it keeps for that account. Clients holding their own session write the record themselves.
Input
namestring, at most 640 bytesWhat the theme is called. palettetheme#paletteOutput
defs#themeViewErrors
InvalidRequest,AuthenticationRequired- POST
/xrpc/dev.knotless.deleteTheme {uri}: deletes one of the account’s own themes, answering{}; 400 for one that is not its ownLexicon
Deletes the dev.knotless.theme record from the account logged in to this appview's web pages. Those who chose it keep its colours, copied into their preferences. Requires the session cookie the pages' login sets.
Input
uristring (at-uri)The theme, one of the account's own. Errors
InvalidRequest,AuthenticationRequired- POST
/xrpc/dev.knotless.updateProfile {displayName, description, pronouns, links, removeAvatar, bot}, each optional: changes the account’sdev.knotless.profileasknotless profiledoes, a field given empty taken out,bottrue saying the account is a bot and false that it is not, answering what it holds now, asgetOwnProfile; 400 for a field too long or a link that is no http or https addressLexicon
Changes the dev.knotless.profile record of the account logged in to this appview's web pages, as
knotless profiledoes: a field left out stays as it is, and one given empty is taken out, so the Bluesky profile's shows again. A profile left with nothing in it is deleted. Requires the session cookie the pages' login sets: the appview writes with the session it keeps for that account. Clients holding their own session write the record themselves.Input
displayNamestring, at most 640 bytes, optionalThe name shown over the account's handle. descriptionstring, at most 2560 bytes, optionalFree text about the account: its bio. pronounsstring, at most 200 bytes, optionalFree-form pronouns text. linksarray of defs#link, at most 10, optionalThe links to show, in order, in place of those there were; none shows the Bluesky profile's. removeAvatarboolean, optionalTakes the picture out, so the Bluesky profile's shows again. dev.knotless.uploadAvatar sets one. botboolean, optionalWhether the account says it is a bot, as dev.knotless.profile's bot. Left out, it stays as it is.Output
getOwnProfile#ownProfileErrors
InvalidRequest,AuthenticationRequired- POST
/xrpc/dev.knotless.uploadAvatar the picture as the whole body, a PNG, JPEG, WebP or GIF: uploads it, made to fit as collaboration.md says, and names it in the profile, answering as
updateProfile; 400 for none, or another kindLexicon
Uploads the body, a PNG, JPEG, WebP or GIF, as a blob of the account logged in to this appview's web pages, and names it as the picture of its dev.knotless.profile record, as
knotless profile --avatardoes: one that is not a PNG or JPEG, or is over 1,000,000 bytes, is drawn again as a JPEG that fits, at most 2000 pixels on its longest side. Requires the session cookie the pages' login sets: the appview writes with the session it keeps for that account.Output
getOwnProfile#ownProfileErrors
InvalidRequest,AuthenticationRequired- POST
/xrpc/dev.knotless.addEmail {email}: claims it, asknotless email add, answering{changed, count}; 400 for one with no@Lexicon
Adds the address's hash to the dev.knotless.emails record of the account logged in to this appview's web pages, as
knotless email adddoes, so commits authored with it show as the account's on repositories it owns or pushes to. Requires the session cookie the pages' login sets: the appview writes with the session it keeps for that account.Input
emailOutput
emailStateErrors
InvalidRequest,AuthenticationRequired- POST
/xrpc/dev.knotless.removeEmail {email}: takes the claim back, answering the sameLexicon
Takes the address's hash out of the dev.knotless.emails record of the account logged in to this appview's web pages, as
knotless email removedoes. Requires the session cookie the pages' login sets: the appview writes with the session it keeps for that account.Input
addEmail#emailOutput
addEmail#emailStateErrors
InvalidRequest,AuthenticationRequired- POST
/xrpc/dev.knotless.blockActor {subject, repo}, a handle or DID, and optionally theat://URI of one of the account’s own repositories: blocks it from all of them or from that one, answering{subject, blocking}; 400 for the account itself, another’s repository or a URI that is no repository’sLexicon
Writes a dev.knotless.block record in the account logged in to this appview's web pages, as
knotless blockdoes, unless one is there already: nothing the account blocked writes about its repositories, or about the one repository named, shows. Requires the session cookie the pages' login sets: the appview writes with the session it keeps for that account. Clients holding their own session write the record themselves.Input
blockOutput
blockStateErrors
InvalidRequest,NotFound,AuthenticationRequired- POST
/xrpc/dev.knotless.unblockActor {subject, repo}: takes those blocks back, answering the sameLexicon
Deletes the dev.knotless.block records of the account logged in to this appview's web pages naming the account, from all its repositories or from the one repository named, as
knotless unblockdoes. Requires the session cookie the pages' login sets: the appview writes with the session it keeps for that account.Input
blockActor#blockOutput
blockActor#blockStateErrors
InvalidRequest,NotFound,AuthenticationRequired- POST
/xrpc/dev.knotless.putPreferences {preferences}: each takes the place of its kind whole, and the rest stay, answering all of them asgetPreferences. A#notificationPref’s reasons not named are on, andfollowedOnlyabsent is false; an#appearancePrefwith nothemefollows the system, and logging in to the pages copies its theme to the browser’sthemecookie, and forcustom, itspaletteto thepalettecookie, and itsnight’s totheme-nightandpalette-night. A#themesPrefholds 50 at most, each a copy, so choosing a theme leaves it be. With a service token too, and with the cookie only from the site; 400 for a kind there is none of, a reason there is none of, a theme not named ina-z,0-9and-(64 at most),customwith no palette, a palette not as its lexicon says (its contrast is not checked here), asourcethat is nodev.knotless.theme, or an added theme whoseuriis nodev.knotless.theme, whose palette is not as its lexicon says, whosenameis over 64 characters, whosecreatoris no DID or whoseaddedAtis no timeLexicon
Changes the asking account's preferences, as the appview keeps them: each one given takes the place of the one of its kind, and the rest stay. Answers them all, as dev.knotless.getPreferences does. Requires authentication: a service token, or the session cookie the pages' login sets, from the pages themselves.
Input
preferencesarray of one of getPreferences#appearancePref, getPreferences#notificationPref, getPreferences#themesPref, at most 32The preferences to set, at most one of each kind. Output
getPreferences#preferencesErrors
InvalidRequest,AuthenticationRequired,NotFound- POST
/xrpc/dev.knotless.createRepo {name, description?, defaultBranch?, private?}: creates a repository in the account logged in, public or withprivatea space of its own, asknotless create [--private]does, answering{uri, name}; 400 for a name no repository can have, one the account has already, or a private one on a PDS without spacesLexicon
Writes a dev.knotless.repo record under the name given in the account logged in to this appview's web pages, as
knotless createdoes: public unless asked for private, naming the runners the appview offers for new repositories, and with no branches until the first push. Requires the session cookie the pages' login sets: the appview writes with the session it keeps for that account. Clients holding their own session write the record themselves.Input
namestring, at most 100 bytesThe repository's name, its record key. descriptionstring, at most 3000 bytes, optionalWhat the repository is, shown under its name. defaultBranchstring, at most 255 bytes, optionalThe branch its HEAD names, without refs/heads/: main when absent. privateboolean, optionalWhether to make it private, as knotless create --privatedoes: a space of its own on the account's PDS, which only its members read, its runners among them. Public when absent.Output
defs#repoMadeErrors
InvalidRequest,AuthenticationRequired- POST
/xrpc/dev.knotless.updateRepo {repo, description?, links?, topics?, defaultBranch?}: asknotless describe, answering{description, links, topics, defaultBranch}as they now stand; 400 for a link that is not an http or https address, or more than five, and for a topic that is not letters, digits and dashes, or more than twentyLexicon
Rewrites the repository's dev.knotless.repo record with a new description, new links, new topics or a new default branch, any of them, as
knotless describedoes. The default branch must be one of its branches, once it has any. Only its owner, logged in to this appview's web pages, can: the appview writes with the session it keeps for that account, which the pages' session cookie names.Input
repostring (at-uri)The repository's dev.knotless.repo record, as dev.knotless.getRepo takes it. descriptionstring, at most 3000 bytes, optionalWhat the repository is; empty drops it. Left as it is when absent. linksarray of defs#link, at most 5, optionalThe links its pages show, in order, each an http or https address: its website first. Replaces those it had; empty drops them. Left as they are when absent. topicsarray of repo#topic, at most 20, optionalWhat it is about, in order, each of lowercase letters, digits and dashes. Replaces those it had; empty drops them. Left as they are when absent. defaultBranchstring, at most 255 bytes, optionalThe branch its HEAD names, without refs/heads/. Left as it is when absent. Output
descriptionstring, optionalWhat it says it is now; absent when it says nothing. linksarray of defs#link, optionalThe links its pages show now; absent when there are none. topicsarray of repo#topic, optionalWhat it is about now; absent when it names nothing. defaultBranchstring, optionalThe branch its HEAD names now. Errors
NotFound,InvalidRequest,AuthenticationRequired,Forbidden- POST
/xrpc/dev.knotless.renameRepo {repo, name}: asknotless rename, answering{uri, name}Lexicon
Moves the repository's dev.knotless.repo record to the new name, as
knotless renamedoes, leaving a dev.knotless.redirect under the old one, so links and clones by it still lead there. Only its owner, logged in to this appview's web pages, can: the appview writes with the session it keeps for that account, which the pages' session cookie names.Input
repostring (at-uri)The repository's dev.knotless.repo record, as dev.knotless.getRepo takes it. namestring, at most 100 bytesIts new name. Output
defs#repoMadeErrors
NotFound,InvalidRequest,AuthenticationRequired,Forbidden- POST
/xrpc/dev.knotless.compactRepo {repo}: asknotless compact, answering{packs, folded, claims}Lexicon
Repacks what the repository's refs reach into one dev.knotless.pack and deletes the packs it replaces, as
knotless compactdoes, with claims a day old. Packs younger than the grace period stay. Only its owner, logged in to this appview's web pages, can: the appview writes with the session it keeps for that account, which the pages' session cookie names.Input
repostring (at-uri)The repository's dev.knotless.repo record, as dev.knotless.getRepo takes it. Output
packsintegerHow many packs it had. foldedintegerHow many of them were folded into one. claimsintegerHow many old claims were dropped. Errors
NotFound,AuthenticationRequired,Forbidden- POST
/xrpc/dev.knotless.transferRepo {repo, to?, name?}: offers it to the accounttoundername(its own if absent), or with notowithdraws the offer, asknotless transfer, answering{transfer}, where it is offered nowLexicon
Names, in the repository's dev.knotless.repo record, the account and name it is offered to, as
knotless transferdoes, or with nototakes the offer back. It moves when that account takes it up (dev.knotless.acceptTransfer). A private repository cannot move. Only its owner, logged in to this appview's web pages, can: the appview writes with the session it keeps for that account, which the pages' session cookie names.Input
repostring (at-uri)The repository's dev.knotless.repo record, as dev.knotless.getRepo takes it. tostring (at-identifier), optionalThe account to offer it to, by handle or DID. Absent, the offer is withdrawn. namestring, at most 100 bytes, optionalThe name it would have there: its own when absent. Output
transferdefs#repoViewBasic, optionalWhere it is offered now; absent once withdrawn. Errors
NotFound,InvalidRequest,AuthenticationRequired,Forbidden- POST
/xrpc/dev.knotless.archiveRepo {repo, unarchive?}: archives it, making it read only, or withunarchivemakes it writable again, asknotless archive, answering{archivedAt}while it is archived; one already as asked is 400Lexicon
Sets or drops
archivedAton the repository's dev.knotless.repo record, asknotless archivedoes. While it is archived, pushes and commits to it are refused, and so is anything more said about it, and runners leave it. Only its owner, logged in to this appview's web pages, can: the appview writes with the session it keeps for that account, which the pages' session cookie names.Input
repostring (at-uri)The repository's dev.knotless.repo record, as dev.knotless.getRepo takes it. unarchiveboolean, optionalTrue to unarchive it, making it writable again. Absent is false. Output
archivedAtstring (datetime), optionalWhen it was archived; absent once unarchived. Errors
NotFound,InvalidRequest,AuthenticationRequired,Forbidden- POST
/xrpc/dev.knotless.acceptTransfer {repo}: asknotless accept, by the account it is offered to, answering{uri, name}where it is nowLexicon
Copies the repository, every branch and tag, into the account logged in to this appview's web pages, under the name its owner offered it by and with the same key, as
knotless acceptdoes: from then on it is there, and readers of the old one follow, with its pulls, issues and forks. Only the account it was offered to can. The appview writes with the session it keeps for that account.Input
repostring (at-uri)The repository's dev.knotless.repo record, as dev.knotless.getRepo takes it. Output
defs#repoMadeErrors
NotFound,InvalidRequest,AuthenticationRequired- POST
/xrpc/dev.knotless.deleteRepo {repo}: asknotless delete, answering{uri}Lexicon
Deletes the repository's dev.knotless.repo record and everything keyed by it (refs, claims, packs, releases, reruns), and, unless another account has taken it up, what its owner wrote about it (their pulls, issues, comments, states, labels, hides, queue entries, blocks, star, and reactions to those), as
knotless deletedoes; for a private one, its space. What others wrote stays in their accounts. It cannot be undone. Only its owner, logged in to this appview's web pages, can: the appview writes with the session it keeps for that account, which the pages' session cookie names.Input
repostring (at-uri)The repository's dev.knotless.repo record, as dev.knotless.getRepo takes it. Output
uristring (at-uri)The record deleted. Errors
NotFound,AuthenticationRequired,Forbidden- POST
/xrpc/dev.knotless.deleteAccountData {did, limit?},didthe account asking: with the pages’ cookie, deletes up tolimit(400 by default) of its knotless records and spaces with the session kept for it, asknotless delete-datadoes, answering{deleted, done}; asked again, it carries on. Oncedone, the server has forgotten the account, as one deleted at its PDS, keeping takedowns and reports, ended its sessions, the pages’ and MCP’s, and logged the browser out. With a service token it deletes nothing, and forgets the account once its PDS holds no knotless record, or answers 400RecordsRemain. 403Forbiddenfor adidnot the asker’sLexicon
Deletes the asking account's knotless data a part at a time, as
knotless delete-datadoes: first the spaces of its private repositories, then each public repository as dev.knotless.deleteRepo deletes one, then whatever is left in its dev.knotless.* collections. Asked by the pages' session cookie, it deletes with the session the appview keeps for the account, up tolimitrecords and spaces a call, and answers whether it is done; the pages ask again until it is, and a run cut short carries on where it stopped. Once nothing is left, the appview drops what it indexed from the account, as for one deleted at its PDS, keeping its takedowns and reports about it, and ends the account's sessions here, the pages' and MCP's, logging the browser out. Asked with a service token, as the command line asks once it has deleted the records itself, it deletes nothing, and forgets the account only once its PDS holds no knotless records. The atproto account itself, and other apps' records in it, are untouched. Only the account itself can ask.Input
didstring (did)The account whose data goes: the one asking, named again so that no call deletes it by mistake. limitinteger, 1 to 1000, default 400, optionalThe most records and spaces to delete in this call. Output
deletedintegerHow many records and spaces this call deleted. donebooleanWhether nothing is left: the appview has then forgotten the account, and logged it out. Errors
AuthenticationRequired,Forbidden,RecordsRemain- POST
/xrpc/dev.knotless.createBranch {repo, name, from?}: asknotless branch, answering{name, sha}Lexicon
Points a new branch at a commit or branch, the default branch when none is named, as
knotless branchdoes. As the account logged in to this appview's web pages: directly for the owner, through the repository's gateway for a collaborator, which refuses anyone else.Input
repostring (at-uri)The repository's dev.knotless.repo record, as dev.knotless.getRepo takes it. namestring, at most 255 bytesThe branch, without refs/heads/. fromstring, at most 255 bytes, optionalThe commit, by hex, or branch it starts at: the default branch when absent. Output
namestringThe branch, without refs/heads/. shastring, optionalThe commit it points at, in hex, once made. Errors
NotFound,InvalidRequest,AuthenticationRequired- POST
/xrpc/dev.knotless.deleteBranch {repo, name}: asknotless branch --delete, answering{name}Lexicon
Deletes a branch, never the default one, as
knotless branch --deletedoes. As the account logged in to this appview's web pages: directly for the owner, through the repository's gateway for a collaborator, which refuses anyone else.Input
repostring (at-uri)The repository's dev.knotless.repo record, as dev.knotless.getRepo takes it. namestring, at most 255 bytesThe branch, without refs/heads/. Output
namestringThe branch, without refs/heads/. shastring, optionalThe commit it points at, in hex, once made. Errors
NotFound,InvalidRequest,AuthenticationRequired- POST
/xrpc/dev.knotless.renameBranch {repo, name, to}: asknotless branch --rename, answering{name}, the new oneLexicon
Makes the new branch where the old one is, points the open pulls into the old one at the new, then deletes the old, as
knotless branch --rename, MCP's rename_branch, does; never the default branch. As the account logged in to this appview's web pages: directly for the owner, through the repository's gateway for a collaborator, which refuses anyone else.Input
repostring (at-uri)The repository's dev.knotless.repo record, as dev.knotless.getRepo takes it. namestring, at most 255 bytesThe branch, without refs/heads/. tostring, at most 255 bytesIts new name. Output
namestringThe branch, without refs/heads/. shastring, optionalThe commit it points at, in hex, once made. Errors
NotFound,InvalidRequest,AuthenticationRequired- POST
/xrpc/dev.knotless.protectBranch {repo, branch, merges?, queue?, reviews?: {approvals?, codeOwners?, dismissStale?}}: asknotless protect, answering{rules}Lexicon
Adds the branch to the protected branches of the repository's dev.knotless.repo record, in place of any rule it had, as
knotless protectdoes: through the gateway it then only moves forward, is never deleted, and unless merges is set takes no merge commits; with queue, pulls land on it only through the merge queue; with reviews, a pull merges into it only once reviewed as they ask. Only its owner, logged in to this appview's web pages, can: the appview writes with the session it keeps for that account, which the pages' session cookie names.Input
repostring (at-uri)The repository's dev.knotless.repo record, as dev.knotless.getRepo takes it. branchstring, at most 255 bytesThe branch, without refs/heads/. mergesboolean, optionalWhether it takes merge commits; otherwise pulls merge into it squashed. queueboolean, optionalWhether pulls land on it only through the merge queue. reviewsrepo#reviews, optionalThe reviews a pull needs before it merges into the branch. Absent, none. Output
rulesarray of repo#ruleThe protected branches now. Errors
NotFound,AuthenticationRequired,Forbidden- POST
/xrpc/dev.knotless.unprotectBranch {repo, branch}: asknotless unprotect, answering{rules}Lexicon
Takes the branch out of the protected branches of the repository's dev.knotless.repo record, as
knotless unprotectdoes. Only its owner, logged in to this appview's web pages, can: the appview writes with the session it keeps for that account, which the pages' session cookie names.Input
repostring (at-uri)The repository's dev.knotless.repo record, as dev.knotless.getRepo takes it. branchstring, at most 255 bytesThe branch, without refs/heads/. Output
rulesarray of repo#ruleThe protected branches now. Errors
NotFound,InvalidRequest,AuthenticationRequired,Forbidden- POST
/xrpc/dev.knotless.defineLabel {repo, name, color?, description?, exclusive?}: asknotless labels add, answering{labels}Lexicon
Adds the label to the repository's dev.knotless.repo record, in place of any of its name, as
knotless labels adddoes. Only its owner, logged in to this appview's web pages, can: the appview writes with the session it keeps for that account, which the pages' session cookie names.Input
repostring (at-uri)The repository's dev.knotless.repo record, as dev.knotless.getRepo takes it. namestring, at most 100 bytesUp to its last /, if it has one, its scope, as kind/bug's is kind.colorstring, at most 7 bytes, optionalA hex color, as #d73a4a. descriptionstring, at most 1000 bytes, optionalWhat the label means, for those who put it on. exclusiveboolean, optionalWhether, of its scope's exclusive labels, it stands alone: setting one takes the others off. Output
labelsarray of repo#labelThe labels it defines now. Errors
NotFound,InvalidRequest,AuthenticationRequired,Forbidden- POST
/xrpc/dev.knotless.removeLabel {repo, name}: asknotless labels remove, answering{labels}Lexicon
Takes the label out of the repository's dev.knotless.repo record, as
knotless labels removedoes: it comes off every pull and issue it was on, and back on them if it is defined again. Only its owner, logged in to this appview's web pages, can: the appview writes with the session it keeps for that account, which the pages' session cookie names.Input
repostring (at-uri)The repository's dev.knotless.repo record, as dev.knotless.getRepo takes it. namestring, at most 100 bytesThe label's name. Output
labelsarray of repo#labelThe labels it defines now. Errors
NotFound,InvalidRequest,AuthenticationRequired,Forbidden- POST
/xrpc/dev.knotless.shareRepo {repo, with, write?}: asknotless share --write, or withwrite: falseasknotless share, letting them only read a private repository, answering{collaborators}; 400 to share for writing before delegating, or a public one for readingLexicon
Names the account among the collaborators of the repository's dev.knotless.repo record, as
knotless share --writedoes: it pushes and commits through the gateway the owner delegated writes to, which must be named first, asknotless delegatenames it. Only its owner, logged in to this appview's web pages, can: the appview writes with the session it keeps for that account, which the pages' session cookie names.Input
repostring (at-uri)The repository's dev.knotless.repo record, as dev.knotless.getRepo takes it. withstring (at-identifier)The account, by handle or DID. writeboolean, optionalWhether it may push, as knotless share --writelets it; false lets it only read a private repository, and open pulls and issues there, asknotless sharedoes, taking back any push it had. True when absent.Output
collaboratorsarray of defs#actorViewThe accounts that may push to it now. Errors
NotFound,InvalidRequest,AuthenticationRequired,Forbidden- POST
/xrpc/dev.knotless.delegateWrites {}: starts delegating the account’s writes to this server’s gateway, asknotless delegatedoes, answering{url, state}, the page where the account approves the gateway’s grantLexicon
Asks this appview's gateway, with a service token the account's PDS mints for the session the appview keeps for it, to take its writes (dev.knotless.putDelegation). The gateway asks the account's authorization server for a grant of its own, of the knotless records and blobs alone, which the account approves at the page this answers with; dev.knotless.awaitDelegation then waits for that. Once approved, sharing a repository for writing names the gateway, through which collaborators push. Requires the session cookie the pages' login sets.
Output
urlstring (uri)The page at the account's authorization server where it approves the grant. statestring, at most 256 bytesWhat names this delegation to dev.knotless.awaitDelegation. Errors
InvalidRequest,AuthenticationRequired- POST
/xrpc/dev.knotless.awaitDelegation {state}: waits up to 25 seconds for that approval, answering{done: false}to ask again, or{done: true, gateway}; 400 for a refused grant or a state not startedLexicon
Asks this appview's gateway whether the delegation is approved (dev.knotless.getDelegation), which waits a while for it before saying it is not yet: ask again until done. Once done, the appview remembers the gateway for the account, so that sharing a repository for writing from the pages names it. A procedure, since it writes that. Requires the session cookie the pages' login sets.
Input
statestring, at most 256 bytesThe delegation, as dev.knotless.delegateWrites named it. Output
donebooleanWhether it was approved: false while it waits still. gatewaystring (did), optionalOnce done, the gateway that now writes for the account. Errors
InvalidRequest,AuthenticationRequired- POST
/xrpc/dev.knotless.inviteToOrg {org, member, role?},membera DID or handle: asknotless org add, by the organization or one of its admins, answering{}. The account’s first invitation makes it an organization, through the gateway it delegated to, or this server’s when its gateway writes for it; 400 for anyone else, or before delegatingLexicon
As the organization itself, or one of its admins, asks the organization's gateway to name the account in its records (dev.knotless.putOrgMember). The first invitation an account makes makes it an organization, through the gateway it delegated writes to. The account is a member once it joins. Requires the session cookie the pages' login sets: the appview acts with the session it keeps for that account. Clients holding their own session call the organization's gateway themselves.
Input
orgstring (did)The organization. memberstring (at-identifier)The account to invite, by DID or handle. rolestring, at most 64 bytes,admin,member, optionalWhat it may do: an admin also invites and removes members. Member when absent. Output
warningstring, at most 2000 bytes, optionalSet when the change was made only in part, saying what is left. Errors
InvalidRequest,AuthenticationRequired- POST
/xrpc/dev.knotless.removeFromOrg {org, member}: asknotless org remove, taking a member out or withdrawing an invitation, answering{}Lexicon
As the organization itself, or one of its admins, asks the organization's gateway to take the account out (dev.knotless.removeOrgMember). Requires the session cookie the pages' login sets: the appview acts with the session it keeps for that account. Clients holding their own session call the organization's gateway themselves.
Input
orgstring (did)The organization. memberstring (did)The account to take out. Output
warningstring, at most 2000 bytes, optionalSet when the change was made only in part, saying what is left. Errors
InvalidRequest,AuthenticationRequired- POST
/xrpc/dev.knotless.setOrgMembership {org, visibility},publicorhidden: asknotless org join, joining as the account logged in, or showing or hiding its membership, answering{}Lexicon
Writes the account's dev.knotless.orgAcceptance record where the visibility says, in its public records or in the organization's space, takes back one in the other place, and asks the organization's gateway to take it in (dev.knotless.joinOrg). Requires the session cookie the pages' login sets: the appview acts with the session it keeps for that account. Clients holding their own session call the organization's gateway themselves.
Input
orgstring (did)The organization. visibilitystring, at most 64 bytes,public,hiddenWho may see the membership: anyone, or the organization's members alone. Output
warningstring, at most 2000 bytes, optionalSet when the change was made only in part, saying what is left. Errors
InvalidRequest,AuthenticationRequired- POST
/xrpc/dev.knotless.endOrgMembership {org}: asknotless org leave, leaving or declining an invitation, answering{warning?}, set when the gateway did not take them outLexicon
Deletes the account's dev.knotless.orgAcceptance records naming the organization, then asks its gateway to take the account out (dev.knotless.leaveOrg). The membership ends with the records: a gateway that does not answer leaves a warning. Requires the session cookie the pages' login sets: the appview acts with the session it keeps for that account. Clients holding their own session call the organization's gateway themselves.
Input
orgstring (did)The organization. Output
warningstring, at most 2000 bytes, optionalSet when the change was made only in part, saying what is left. Errors
InvalidRequest,AuthenticationRequired- POST
/xrpc/dev.knotless.deleteOrg {}: asknotless org delete --yes, ending the organization the account logged in is, answering{}Lexicon
Takes out every member, withdraws every invitation, deletes the organization's space and its dev.knotless.org record. The account and its repositories stay. Requires the session cookie the pages' login sets: the appview acts with the session it keeps for that account. Clients holding their own session call the organization's gateway themselves.
Output
warningstring, at most 2000 bytes, optionalSet when the change was made only in part, saying what is left. Errors
InvalidRequest,AuthenticationRequired- POST
/xrpc/dev.knotless.unshareRepo {repo, with}: asknotless unshare, answering{collaborators}Lexicon
Takes the account out of the collaborators of the repository's dev.knotless.repo record, as
knotless unsharedoes, and for a private repository out of its readers. Only its owner, logged in to this appview's web pages, can: the appview writes with the session it keeps for that account, which the pages' session cookie names.Input
repostring (at-uri)The repository's dev.knotless.repo record, as dev.knotless.getRepo takes it. withstring (at-identifier)The account, by handle or DID. Output
collaboratorsarray of defs#actorViewThe accounts that may push to it now. Errors
NotFound,InvalidRequest,AuthenticationRequired,Forbidden- POST
/xrpc/dev.knotless.setTriager {repo, who, scopes}: asknotless triagers add, answering{triagers}Lexicon
Names the account among the triagers of the repository's dev.knotless.repo record, for the scopes given, in place of what it had, as
knotless triagers adddoes: its labels of those scopes count, below the owner's and collaborators'. Only its owner, logged in to this appview's web pages, can: the appview writes with the session it keeps for that account, which the pages' session cookie names.Input
repostring (at-uri)The repository's dev.knotless.repo record, as dev.knotless.getRepo takes it. whostring (at-identifier)The account, by handle or DID. scopesarray of repo#scope, at most 50The scopes of the labels it may put on, at least one. Output
triagersarray of getRepoSettings#triagerViewThe repository's triagers now. Errors
NotFound,InvalidRequest,AuthenticationRequired,Forbidden- POST
/xrpc/dev.knotless.removeTriager {repo, who}: asknotless triagers remove, answering{triagers}Lexicon
Takes the account out of the triagers of the repository's dev.knotless.repo record, as
knotless triagers removedoes. Only its owner, logged in to this appview's web pages, can: the appview writes with the session it keeps for that account, which the pages' session cookie names.Input
repostring (at-uri)The repository's dev.knotless.repo record, as dev.knotless.getRepo takes it. whostring (at-identifier)The account, by handle or DID. Output
triagersarray of getRepoSettings#triagerViewThe repository's triagers now. Errors
NotFound,InvalidRequest,AuthenticationRequired,Forbidden- POST
/xrpc/dev.knotless.forkRepo {repo, name?}: forks it into the account logged in, under its own name orname, asknotless forkdoes, answering{uri, name}; 404 for no such repository, 400 for a name the account has alreadyLexicon
Copies a repository, every branch and tag, into a new dev.knotless.repo in the account logged in to this appview's web pages, naming it as forked from the first, as
knotless forkdoes. Requires the session cookie the pages' login sets: the appview writes with the session it keeps for that account.Input
repostring (at-uri)The repository to fork: its dev.knotless.repo record, as dev.knotless.getRepo takes it. namestring, at most 100 bytes, optionalThe fork's name: the repository's own when absent. Output
defs#repoMadeErrors
NotFound,InvalidRequest,AuthenticationRequired- POST
/xrpc/dev.knotless.uploadImage?repo= a picture as the whole body, a PNG, JPEG, WebP or GIF: uploads it to the account logged in, made to fit, to show in a pull, issue or comment on
repo, answering{blob, url, width, height}, the last two the size it is drawn at in CSS pixels (collaboration.md); 400 for anything else. Its PDS keeps it once a write below attaches itLexicon
Uploads the body, a PNG, JPEG, WebP or GIF, as a blob of the account logged in to this appview's web pages, as
knotless issue --imagedoes: one over 1,000,000 bytes is drawn again as a JPEG that fits, at most 2000 pixels on its longest side. Its PDS keeps it only once a record attaches it: give it as one of dev.knotless.createIssue's, createPull's, createComment's or editItem'simages, with a body linking tourl. Requires the session cookie the pages' login sets.Parameters
repostring (at-uri)The repository's dev.knotless.repo record, as dev.knotless.getRepo takes it, whose pull, issue or comment will show it. Output
blobblobThe picture as uploaded, to attach. urlstring (uri)Where its account's PDS serves it, for the body to link to. widthinteger, optionalHow wide it is drawn, in CSS pixels, as dev.knotless.defs#image's width: its pixels over the density a PNG says it was made at. Give it, andheight, with the blob inimages.heightinteger, optionalHow high it is drawn, in CSS pixels. Errors
NotFound,InvalidRequest,AuthenticationRequired- POST
/xrpc/dev.knotless.createIssue {repo, title, body?, images?, labels?}: opens an issue as the account logged in, answering{uri}, withlabels(each{name}) put on aslabelItemwould; 400, with the template, for a body that follows none of the repository’s, and, before anything is written, for a label the account may not put onLexicon
Writes a dev.knotless.issue record in the account logged in to this appview's web pages, as
knotless issuedoes, and a dev.knotless.label record about it when labels are chosen: refused, with the template, when the repository has one (see dev.knotless.getTemplate) and the body follows none of it. Requires the session cookie the pages' login sets.Input
repostring (at-uri)The repository's dev.knotless.repo record, as dev.knotless.getRepo takes it. titlestring, at most 3000 bytesbodystring, at most 20000 bytes, optionalWhat it is about, in Markdown. imagesarray of defs#image, at most 32, optionalPictures uploaded with dev.knotless.uploadImage; those the body links to are attached. labelsarray of defs#labelName, at most 32, optionalLabels to put on it as it opens, as dev.knotless.labelItem would: refused, with nothing written, when one is not the repository's or not the account's to put on. Output
uristring (at-uri)The dev.knotless.issue record written. Errors
NotFound,AuthenticationRequired- POST
/xrpc/dev.knotless.createPull {repo, base, head, branch, title, body?, images?, draft?, labels?, reviewers?}: opens a pull to mergebranchofhead, this repository or a fork, intobase, as a draft withdraft, answering{uri}, withlabelsput on ascreateIssuedoes andreviewers(each{did}) asked to review it; 400 for a branch on neither, a body that follows none of the template, or a label the account may not put onLexicon
Writes a dev.knotless.pull record in the account logged in to this appview's web pages, as
knotless pulldoes: asking to merge a branch of this repository or a fork into one of its branches, both of which must exist. Refused, with the template, when the repository has one at the base (see dev.knotless.getTemplate) and the body follows none of it. Requires the session cookie the pages' login sets.Input
repostring (at-uri)The repository to merge into: its dev.knotless.repo record, as dev.knotless.getRepo takes it. basestring, at most 1024 bytesThe branch to merge into. headstring (at-uri)The repository the branch is on: this one, or a fork. branchstring, at most 1024 bytesThe branch to bring in. titlestring, at most 3000 bytesbodystring, at most 20000 bytes, optionalWhat it does and why, in Markdown. imagesarray of defs#image, at most 32, optionalPictures uploaded with dev.knotless.uploadImage; those the body links to are attached. labelsarray of defs#labelName, at most 32, optionalLabels to put on it as it opens, as dev.knotless.labelItem would: refused, with nothing written, when one is not the repository's or not the account's to put on. reviewersarray of pull#reviewer, at most 32, optionalThe accounts its author asks to review it, as its record names them; requestedAtis set as it is written.draftboolean, optionalOpen it as a draft, not ready to merge or queue. Not, when absent. Output
uristring (at-uri)The dev.knotless.pull record written. Errors
NotFound,AuthenticationRequired- POST
/xrpc/dev.knotless.createComment {repo, subject, body, verdict?, images?, lines?}: comments on a pull or issue as the account logged in, or withverdict(approve,changes) reviews a pull;lines, each{path, side?, line, startLine?, body}, comment on the pull’s diff, as the review’s when it has a body or verdict. A comment assubjectanswers it in its thread. Answers the first it wrote’s{uri}; 400 for a comment that says nothingLexicon
Writes a dev.knotless.comment record in the account logged in to this appview's web pages, as
knotless commentdoes: with a verdict, a review of the pull's head as it is now. Requires the session cookie the pages' login sets: the appview writes with the session it keeps for that account. Clients holding their own session write the record themselves.Input
repostring (at-uri)The repository's dev.knotless.repo record, as dev.knotless.getRepo takes it. subjectstring (at-uri)The dev.knotless.pull or dev.knotless.issue commented on; or a dev.knotless.comment to answer, in the thread it starts or is in. bodystring, at most 20000 bytesWhat it says, in Markdown. imagesarray of defs#image, at most 32, optionalPictures uploaded with dev.knotless.uploadImage; those the body links to are attached. verdictstring, at most 64 bytes,approve,changes, optionalFor a review of a pull: approve, orchangesto ask for changes.linesarray of lineComment, at most 100, optionalComments on lines of a pull's diff, at its head as it is now, each starting a thread, written at once: as the review's when it has a body or a verdict, else on their own. Output
uristring (at-uri)The dev.knotless.comment record written: with lines, the review, or without one the first line comment. Errors
NotFound,AuthenticationRequired- POST
/xrpc/dev.knotless.editItem {repo, subject, title?, body?, images?, draft?}: changes the account’s own pull, issue or comment, answering{uri};draft, for a pull alone, makes it a draft or withfalsemarks it ready.images, each{image, width?, height?}with a blob and sizeuploadImagegave, are attached to what the body links to (collaboration.md). It,createIssue,createPullandcreateCommentwrite each@handleof the title and body that resolves as a facet naming its DID (collaboration.md)Lexicon
Rewrites a dev.knotless.pull, dev.knotless.issue or dev.knotless.comment record in the account logged in to this appview's web pages, which must have written it, as
knotless editdoes: a pull's or an issue's title or body, or a comment's body, and whether a pull is a draft. An empty body takes a pull's or an issue's away; a comment keeps one. Requires the session cookie the pages' login sets.Input
repostring (at-uri)The repository's dev.knotless.repo record, as dev.knotless.getRepo takes it. subjectstring (at-uri)The pull, issue or comment to change. titlestring, at most 3000 bytes, optionalA pull's or an issue's new title. bodystring, at most 20000 bytes, optionalIts new body, in Markdown. imagesarray of defs#image, at most 32, optionalPictures uploaded with dev.knotless.uploadImage for the new body. Of these and those it had, the new body's links decide which stay attached. draftboolean, optionalFor a pull, make it a draft, or with false mark it ready to merge. Left as it is, when absent; any other item refuses it. Output
uristring (at-uri)The record changed. Errors
NotFound,AuthenticationRequired- POST
/xrpc/dev.knotless.deleteComment {repo, subject}: deletes the account’s own comment or review, answering{uri}Lexicon
Deletes a dev.knotless.comment record from the account logged in to this appview's web pages, which must have written it, as
knotless delete-commentdoes. A pull or an issue is closed instead, and what others say the repository's owner hides. Requires the session cookie the pages' login sets.Input
repostring (at-uri)The repository's dev.knotless.repo record, as dev.knotless.getRepo takes it. subjectstring (at-uri)The dev.knotless.comment record to delete. Output
uristring (at-uri)The record deleted. Errors
NotFound,AuthenticationRequired- POST
/xrpc/dev.knotless.setItemState {repo, subject, state}: closes (closed) or reopens (open) a pull or issue, or resolves (resolved) or opens (open) the thread a comment on a pull’s lines starts, answering the state record’s{uri}Lexicon
Writes a dev.knotless.state record in the account logged in to this appview's web pages, as
knotless close,reopen,resolveandunresolvedo. It counts if that account opened the pull or issue (or the thread), or owns or collaborates on the repository. Requires the session cookie the pages' login sets.Input
repostring (at-uri)The repository's dev.knotless.repo record, as dev.knotless.getRepo takes it. subjectstring (at-uri)The dev.knotless.pull or dev.knotless.issue; or the dev.knotless.comment starting a thread on a pull's lines. statestring, at most 64 bytes,open,closed,resolvedWhat it is to be: closed, oropenagain; for a thread,resolved, oropenagain. Merging a pull is another thing.Output
uristring (at-uri)The dev.knotless.state record written. Errors
NotFound,AuthenticationRequired- POST
/xrpc/dev.knotless.labelItem {repo, subject, label, removed?}: puts a label on, or takes it off, answering the{labels}it carries nowLexicon
Rewrites the dev.knotless.label record about a pull or an issue in the account logged in to this appview's web pages, as
knotless labelandknotless unlabeldo: the repository's owner and collaborators put on any label it defines, and its triagers those of their scopes. Putting on an exclusive label takes the others of its scope off. Requires the session cookie the pages' login sets.Input
repostring (at-uri)The repository's dev.knotless.repo record, as dev.knotless.getRepo takes it. subjectstring (at-uri)The dev.knotless.pull or dev.knotless.issue. labelstring, at most 100 bytesThe name of a label the repository defines. removedboolean, default false, optionalTrue to take it off rather than put it on. Output
labelsarray of repo#labelThe labels it carries now, in the order the repository defines them. Errors
NotFound,AuthenticationRequired- POST
/xrpc/dev.knotless.hideItem {repo, subject, hidden?}: as the owner, hides a pull, issue or comment, or withhidden: falseshows it again, answering{hidden}Lexicon
Writes a dev.knotless.hide record naming it in the account logged in to this appview's web pages, which must own the repository, as
knotless hidedoes; or, with hidden false, deletes those there are, asknotless unhidedoes. Requires the session cookie the pages' login sets.Input
repostring (at-uri)The repository's dev.knotless.repo record, as dev.knotless.getRepo takes it. subjectstring (at-uri)The dev.knotless.pull, dev.knotless.issue or dev.knotless.comment. hiddenboolean, default true, optionalFalse to show it again. Output
hiddenbooleanWhether it is hidden now. Errors
NotFound,AuthenticationRequired- POST
/xrpc/dev.knotless.renderMarkdown {text, repo?}: the HTML the pages showtextas once posted, relative links inside the repositoryreponames and each@handlea link to its profile, answering{html}; needs no login, and 400 for text past 20000 bytes, a body’s longestLexicon
Renders Markdown to HTML with the renderer the pages draw comments, issue and pull bodies with: raw HTML shown as text, bare web addresses made links, and relative links resolved inside the repository. Reads and fetches nothing, and needs no login. A procedure rather than a query, since the text may be long.
Input
textstring, at most 20000 bytesThe Markdown, as long as a comment's or a body's may be. repostring (at-uri), optionalThe dev.knotless.repo record of the repository it is written in, as dev.knotless.getThread takes it: relative links resolve inside it. Without it, they resolve from the site's root. Output
htmlstringThe HTML, as dev.knotless.getThread's comments carry it. - POST
/xrpc/dev.knotless.createReport {subject, reason, text?}: reports a handle,knotless://URL orat://URI to the moderation service, answering{reportId}Lexicon
Sends com.atproto.moderation.createReport to the moderation service this appview names, with a service token from the account logged in to its web pages, as
knotless reportdoes. Requires the session cookie the pages' login sets.Input
subjectstring, at most 8192 bytesWhat is reported: an account's handle or DID, a repository's knotless:// URL, or the at:// URI of a record, such as a pull, an issue or a comment. reasonstring, at most 64 bytes,spam,violation,misleading,sexual,rude,otherWhy, as com.atproto.moderation.defs names reasons, by the last word of each. textstring, at most 20000 bytes, optionalMore on why, for the moderators. Output
reportIdintegerThe report's number, as the moderation service gave it. Errors
AuthenticationRequired- POST
/xrpc/dev.knotless.addReaction {repo, subject, content}: reacts to a pull, issue or comment the repository’s pages show, as the account logged in, answering{subject, reactions}; 400 for a content not among the eight, 404NotFoundfor a subject the pages do not showLexicon
Writes a dev.knotless.reaction record naming the subject, with the content, in the account logged in to this appview's web pages, unless one is there already. Requires the session cookie the pages' login sets: the appview writes with the session it keeps for that account. Clients holding their own session write the record themselves.
Input
repostring (at-uri)The repository's dev.knotless.repo record, as dev.knotless.getThread takes it. subjectstring (at-uri)The dev.knotless.pull, dev.knotless.issue or dev.knotless.comment record on that repository. contentdefs#reactionContentOutput
defs#reactionStateErrors
NotFound,AuthenticationRequired- POST
/xrpc/dev.knotless.removeReaction {repo, subject, content}: takes the account’s reactions of that content to it back, answering{subject, reactions}Lexicon
Deletes every dev.knotless.reaction record naming the subject with the content in the account logged in to this appview's web pages. Requires the session cookie the pages' login sets.
Input
repostring (at-uri)The repository's dev.knotless.repo record, as dev.knotless.getThread takes it. subjectstring (at-uri)The dev.knotless.pull, dev.knotless.issue or dev.knotless.comment record on that repository. contentdefs#reactionContentOutput
defs#reactionStateErrors
NotFound,AuthenticationRequired
Changing the profile, emails and blocks needs the pages’ cookie, as starring does, and writes with the session kept for the account. A change to the profile, or a block from all repositories, is indexed at once, so the next page shows it.
updateRepo, renameRepo, compactRepo, transferRepo, archiveRepo, deleteRepo,
protectBranch, unprotectBranch, defineLabel, removeLabel,
shareRepo, unshareRepo, setTriager and removeTriager are the
owner’s alone: anyone else logged in is 403 Forbidden, whatever
the pages show them. createBranch, deleteBranch and renameBranch
push as the account logged in, as knotless branch does: a collaborator
through the gateway, which refuses anyone else. getRepo’s
viewer.transfer names where the repository is offered, when it is
offered to the asker.
Starring, following, reacting, writing on pulls and issues and changing a
repository need the pages’ cookie: a service token, or none, is 401
AuthenticationRequired. What the knotless crate refuses to write, as
a comment on what is no pull or issue, an edit of someone else’s comment,
a label its writer may not set or a default branch the repository does
not have, is 400 InvalidRequest in its words. Starring a private repository is 404 NotFound,
as a star is public; a reaction on one is written in its space.
Landing and checks
Section titled “Landing and checks”A pull’s page lands it, and the checks pages run its workflows, through
these writes, as the account logged in, with the session the server keeps
for it, through the same code as the command line. Each needs the pages’
cookie as starring does (401 without it, 403 from another site), and
answers a refusal 400 InvalidRequest in the command’s words: a pull that
conflicts, checks that have not passed, an account that may not. A
private repository’s are made and read as the member logged in.
| Route | |
|---|---|
POST /xrpc/dev.knotless.mergePull | {repo, pull, keepBranch}: merges it as knotless merge does, waiting for no checks, answering {commit, deleted, closed: [{uri}]}; 400 into a branch that takes pulls only through the merge queue, which queuePull queues it into |
POST /xrpc/dev.knotless.revertPull | {repo, pull, reason}: as the owner or a collaborator, reverts a merged pull as knotless revert does, answering {uri, branch, commit}, uri the pull it opens, which is indexed at once; 400 on a pull not merged, or whose undoing conflicts. getThread’s viewer.revert says who may |
POST /xrpc/dev.knotless.queuePull | {repo, pull}: puts it in its branch’s merge queue, as knotless merge --queue, answering {base, position, of}; 400 while the queue lands through this server’s gateway and the owner has not delegated writes to it |
POST /xrpc/dev.knotless.leaveQueue | {repo, pull}: takes it out, as knotless queue --leave, answering {} |
POST /xrpc/dev.knotless.retargetPull | {repo, pull, base, head}: points it at other branches, either or both, as knotless retarget, answering {} |
POST /xrpc/dev.knotless.requestRerun | {repo, pull} or {repo, ref}, with workflow, and at a ref env: [{name, value}]: as the owner, asks the runners to run them again, as knotless rerun, answering {uri}, the dev.knotless.rerun record |
POST /xrpc/dev.knotless.addRunner | {repo, runner}, a handle or DID: as the owner, names it a runner, as knotless runners add, answering {runners, warning}, the warning when a private repository could not tell it |
POST /xrpc/dev.knotless.removeRunner | {repo, runner}: as the owner, names it no longer, answering {runners} |
getMergeStatus answers someone asking, by the cookie or a service token,
from what they may read of the repository, a private one read as them, as
listWorkflows and listRunners are: state (clean,
conflicting with conflicts: [{path}], blocked by its checks or
its reviews, or draft, whatever else holds, until its author marks it
ready), and
reason, why not, in a merge’s words, and that the asker may not merge
it when it may not; checks (passed, failed,
running, pending, or none when the repository names no runners or
has no workflow that runs on the pull);
method (fastForward, merge, squash into a linear branch);
deletes, the branch it would delete; base and head, the commits; and
may, what the asker’s part in the repository lets it do, not whether
that would work now: merge and queue (owner and
collaborators, the queue only with runners), force (owner), base
(those who may close it), head (its author), rerun (owner, with
runners); and queueBlocked: "undelegated" when the asker may queue it but
no runner the repository names may push, so its queue lands through this
server’s gateway, and the owner has not delegated writes to it; and
queueOnly: true when its branch takes pulls only through the merge
queue, where mergePull refuses it and the pages queue it with
queuePull; reviews (passed, blocked, or none when its branch asks
for none), reviewsMissing: [{text}], what they lack,
reviewsUncounted: [{text}], the approvals that do not count and why, and
codeOwnersWarnings: [{text}], the CODEOWNERS lines skipped, as knotless merge --check prints them. A merge, queue or retarget is indexed at once, so the page after
shows it.
ref and path together are split at the longest branch or tag name that
starts them, else after the ref’s first segment. The pictures are sent as
they are, never as a page. Served beside them, not as queries:
| Route | |
|---|---|
/<authority>/<repo>/raw/<rev>/<path> | a file’s bytes |
/<authority>/<repo>/releases/download/<package>/<version>/<file> | a file of a release the index has, application/octet-stream, Content-Disposition: attachment with its name, Content-Length, its SHA-256 as ETag (a matching If-None-Match is 304), read once from the owner’s PDS, checked against its record and kept, as /releases/ keeps its own (packages.md); not compressed. One the index does not have, taken down, or of a private repository is 404, and one that cannot be read 502 |
/card.png, /<authority>/<repo>/card.png, /<authority>/<repo>/<pull or issue>/<did>/<rkey>/card.png | social cards, 1200×630 PNG |
These routes but the release files are compressed (gzip or br). Answers
are Cache-Control: no-cache, even at a commit hash, and a failure
no-store. A raw file, release file or
card asked for by an old name answers 307 to the same path under the new
name, and the new owner if it moved, keeping the query.