Skip to content

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.

StatusWhen
400a bad parameter, or a bad repository name
401a missing or bad service token
403a 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)
502the PDS or identity lookup failed

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).

Answers ok while the process runs.

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
okeverything holds
problemsstrings, below; dead feeds are listed even while ok is true
uptime_secondssince the process started
feeds[]name, url, ok, connected, silent_seconds, stale_after_seconds, events, seq
indexok, error; with --index
mirrorrepo, 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>s
  • no feed is bringing events
  • index: <error>
  • mirror <repo>: <error>, including ?repo= takes <handle or did>/<name>
Route
GET /<authority>/<repo>/info/refs?service=git-upload-packthe ref advertisement
POST /<authority>/<repo>/git-upload-packa 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.

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.

RouteParametersAnswer
GET /refs{refs: [{name, target}], head}
GET /treerev, path{rev, path, entries: [{name, mode, type, sha, size}]}
GET /blobrev, paththe raw bytes, application/octet-stream, ETag the blob’s hash
GET /logrev, path, limit (default 50, at most 1000){rev, commits: [{sha, parents, author: {name, email, date}, message}]}
GET /diffbase, headgit 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.

XRPC queries under /xrpc/. All but getProfile, describeServer and getStats need --index, and without one answer 404 this server keeps no index.

ParameterDefault
qmatched against name and description
runnera DID: only repositories whose record names it in runners
triagera DID: only repositories whose record names it in triagers
topiconly repositories whose record names it in topics
limit50clamped to 1 to 500
cursorfrom 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, 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, optional
cursorstring, at most 512 bytes, optional

Output

cursorstring, at most 512 bytes, optional
reposarray of repoView

dev.knotless.listRepos.json

ParameterDefault
qrequired; matched against the handle, ignoring case
limit10clamped 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 bytesText the handle holds.
limitinteger, 1 to 50, default 10, optional

Output

ownersarray of ownerView

dev.knotless.searchOwners.json

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, optionalThe 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, optionalThe oldest client version this appview asks for. Absent, it asks for none.

dev.knotless.describeServer.json

ParameterDefault
repothe repository’s at:// URI; only its owner’s DID is used
keythe repository’s key
limit500clamped to 1 to 1000
cursorthe 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), optionalThe last URI of the page before.

Output

cursorstring (at-uri), optionalAbsent on the last page.
entriesarray of entry

dev.knotless.getForge.json

ParameterDefault
repo, keyas for getForge
refonly checks of this ref
commitonly checks of this commit
pullonly checks of this pull’s at:// URI
limit50clamped to 1 to 100
cursorfrom 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, optionalOnly checks of this ref, named in full, such as refs/heads/main.
commitstring, at most 64 bytes, optionalOnly checks of this commit, its hex object ID.
pullstring (at-uri), optionalOnly the runs of this pull's head.
limitinteger, 1 to 100, default 50, optional
cursorstring, at most 2048 bytes, optionalThe cursor of the page before.

Output

cursorstring, at most 2048 bytes, optionalAbsent on the last page.
checksarray of check

dev.knotless.listChecks.json

actor

only what this DID did

filter

Default everyone

following: what the account asking and those it follows did, and anything done on its repositories and those it starred

kind

Default every kind

only pushes (refs pushed and deleted), pulls or issues (opened and moved between states), comments, stars or repos (created and forked); a kind it does not know answers nothing

limit

Default 40

clamped to 1 to 100

cursor

from the page before

Answers events (createdAt, actor {did, handle, avatar?}, repo {did, handle, name}, action) and cursor. action.$type is one of:

$typeFields
dev.knotless.getFeed#createddescription?, forkOf?
dev.knotless.getFeed#pushedref, created, to?, forced?, count, new, commits (sha, message, merge?)
dev.knotless.getFeed#deletedref
dev.knotless.getFeed#openedsubject, title
dev.knotless.getFeed#commentedsubject, title, text, verdict?
dev.knotless.getFeed#changedsubject, title, state
dev.knotless.getFeed#starredsubject, 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, optionalWhere the page starts: a previous page's cursor.
actorstring (did), optionalOnly 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", optionalWhose 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, optionalOnly 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, optionalWhere the next page starts, when there may be one.

dev.knotless.getFeed.json

ParameterDefault
actorthe account’s DID
sincea year before nowthe first day counted is the one this time falls on in tz
tzUTCthe 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), optionalThe first day counted is the one this falls on in tz; a year before now if not given.
tzstring, at most 64 bytes, optionalThe IANA time zone whose days are counted, as America/New_York; UTC if not given or not known.

Output

daysarray of dayViewOldest first.
reposarray of repoActivityEach of the account's repositories ever pushed to, by name.

dev.knotless.getActorActivity.json

Parameter
actora 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

dev.knotless.getProfile.json

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 dayThe last 30 days, oldest first, today (UTC) the last.
networkgetUsage#network, optionalAbsent where the appview keeps no index.

dev.knotless.getStats.json

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

Parameters repo: the repository’s at:// URI

Events changed, its data null, 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

Parameters repo and uri, as dev.knotless.getCheck; the Last-Event-ID header, where the output was had to

Events output: what the run printed since, a JSON string, its id the 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 data null, once, as the run’s steps are over

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

Auth service token, lxm this method

Parameters subject, reasonType, reason?

Answer the stored report: id, reasonType, subject, reportedBy, createdAt, reason?

GET dev.knotless.getLogs

Auth admin

Parameters trace, or any of message, level (that or above), minMs; limit (default 500 for a trace, else 100; clamped to 1 to 500); needs --logs

Answer lines, oldest first, each {line}, the JSON line as logged; 400 asking for none of them, 404 keeping no logs

Lexicon

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.

dev.knotless.getLogs.json

GET dev.knotless.listReports

Auth admin

Parameters limit (default 50, clamped to 1 to 500), cursor (an id: reports before it)

Answer reports, newest first, and cursor when the page is full

Lexicon

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, optional
cursorstring, optionalA report id: those before it.

Output

cursorstring, optional
reportsarray of report

dev.knotless.listReports.json

POST com.atproto.admin.updateSubjectStatus

Auth admin

Parameters subject, takedown.applied

Answer subject, takedown

GET com.atproto.admin.getSubjectStatus

Auth admin

Parameters did or uri

Answer subject, takedown.applied

GET com.atproto.label.queryLabels

Parameters uriPatterns, repeatable: a URI, or a prefix ending in *

Answer labels

GET com.atproto.label.subscribeLabels

Parameters cursor

Answer a WebSocket of labels

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

Auth service token, lxm this method

Parameters runner (a DID, else 400), name (a repository name, else 400): the caller’s repository

Answer {}; an invite already there moves to the end

Lexicon

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.

dev.knotless.inviteRunner.json

GET dev.knotless.listRunnerInvites

Auth service token, lxm this method

Parameters limit (default 100, clamped to 1 to 500), cursor (one this method gave, else 400)

Answer the caller’s invites after cursor, oldest first, each did, name, createdAt; and cursor, where the page ended, the one given when it is empty

Lexicon

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, optional
cursorstring, 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

dev.knotless.listRunnerInvites.json

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

Auth service token, lxm this method, or the pages’ login

Parameters repo (a key: 1 to 32 letters and digits), runner (a DID), name (a shell variable’s name, not starting KNOTLESS_RUN), refs (at most 16 {pattern}, each 1 to 256 bytes), key (a did:key), sealed (1 to 16465 bytes); any else 400

Answer {}; one of the same repository, runner and name is replaced

Lexicon

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 secrets lists 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 refs match 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, name and refs: the encapsulated key, then the ciphertext.

dev.knotless.putSecret.json

POST dev.knotless.deleteSecret

Auth service token, lxm this method, or the pages’ login

Parameters repo; name, runner, or both, to delete only those

Answer deleted, how many of the caller’s went

Lexicon

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.

dev.knotless.deleteSecret.json

GET dev.knotless.listSecrets

Auth service token, lxm this method, or the pages’ login

Parameters owner, repo

Answer secrets, by name then runner, each name, runner, refs, key, updatedAt: every one to the owner, with sealed only on those sealed to it; those sealed to the caller, with sealed, to anyone else

Lexicon

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

dev.knotless.listSecrets.json

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.

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

Auth service token, aud the server’s #knotless_appview, lxm this method

Input space; the rest is not read

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

Auth the same

Input space

Answer {}; the space is forgotten, with every record it gave

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

Auth service token, lxm this method

Input check: a dev.knotless.check record of the caller’s, else 400; startedAt?, the run’s start, as its record says: another than the run kept started at starts it anew; offset; text?, at most 20,000 bytes; steps?; going?, the step running now, taken with steps; ended?

Answer {watched}: whether a page follows the run now; 429 while the server holds 1,024 runs, or 128 of the caller’s

Lexicon

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 text starts 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.

dev.knotless.putRunOutput.json

GET dev.knotless.awaitSpaceWrites

Auth service token, lxm this method

Input cursor?

Answer cursor, and writes, each space: those written to after cursor of 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, and missed: true

Lexicon

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 write
missedboolean, optionalSet when cursor was none, or of no place the appview remembers, as after it restarted: any space may have been written to since.

dev.knotless.awaitSpaceWrites.json

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.

RouteInputAnswer
GET /.well-known/did.jsonthe did:web document, with #knotless_gateway, #atproto_labeler and #knotless_appview services
GET /oauth-client-metadata.jsonthe gateway’s OAuth client metadata
GET /oauth/callbackthe authorization server’s redirecta page saying whether the gateway now writes for you
GET /oauth/bot-client-metadata.jsonthe bots’ OAuth client metadata; with --bots
GET /oauth/botaccount, a handle or DID that KNOTLESS_BOT_DIDS namesa redirect to approve a bot’s login at its PDS; with --bots
GET /oauth/bot-callbackthe authorization server’s redirecta page saying whether the bot is logged in; with --bots
POST dev.knotless.putDelegation{url, state}
GET dev.knotless.getDelegationstate{done}
POST dev.knotless.pushRefsa 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.

Served with --public-url, to any account the index has not taken down. The server is its own OAuth authorization server, with PKCE.

Route
/mcpthe 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/mcpprotected-resource metadata
GET /.well-known/oauth-authorization-serverauthorization-server metadata
POST /mcp/registerclient registration (RFC 7591)
GET, POST /mcp/authorizesign-in, with atproto OAuth at the user’s PDS
GET /mcp/oauth/callbackwhere the user’s PDS sends them back
POST /mcp/tokentokens
GET /mcp/oauth-client-metadata.jsonthe client metadata it signs users in with

Registrations and tokens are signed, not stored.

Lifetime
Access token1 hour
Refresh token90 days
An account’s session, from its last token90 days
Client registration100 years
Sign-in15 minutes
Authorization code5 minutes
Limit
Tool calls running, per account8
Of those, with a wait2
Tool calls started a minute, per account120
Sign-ins waiting on PDSes, from everyone1000

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.

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 at X-Forwarded-Proto (default http) and Host (default 127.0.0.1:2590)

GET /releases/...

the directory: latest, and <version>/knotless-<version>-<target>.tar.gz with 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 release with 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’s serve/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.

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, {} and Docker-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}, anonymous with no credentials, which pulls need; with Basic credentials, a handle (or DID) and an app password, one to push with for an hour, expires_in 3600, made by logging in to their PDS and kept only in memory; a password the PDS refuses is 401

GET, HEAD /v2/<name>/manifests/<tag or digest>

the manifest: by tag, the one its release’s index.json names; by digest, the file of that digest among the package’s releases; as the media type it says (an OCI image manifest if none), with Docker-Content-Digest

GET, HEAD /v2/<name>/blobs/<digest>

the blob, application/octet-stream, with Docker-Content-Digest and Content-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, and latest if there is one

POST /v2/<name>/blobs/uploads/

begins an upload: 202 with Location and Docker-Upload-UUID; with ?digest=, the body is the whole blob, ended as a PUT ends 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 Location and Docker-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.json naming 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.

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, optional
cursorstring, at most 512 bytes, optional

Output

cursorstring, at most 512 bytes, optional
reposarray of repoView

dev.knotless.listRepos.json

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, optional

Output

ownersarray of ownerView

dev.knotless.searchOwners.json

GET dev.knotless.getActorRepos?actor=

an account’s public repositories, each with the language most 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, each private, and to a member asking those it was proven a member of; asked as the account itself, apart, as shared, the private repositories of others it was proven a member of

Lexicon

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#repoView
sharedarray 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

dev.knotless.getActorRepos.json

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) and collaboratorCount, private if it is, and archivedAt if its owner archived it; with an asker, viewer: its star, a transfer offered to it, and the labels it may put on the repository’s pulls and issues

Lexicon

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#repoViewDetailed

Errors

NotFound

dev.knotless.getRepo.json

GET dev.knotless.listReleases?repo=&package=&limit=&cursor=

its releases (packages.md), newest first, 30 a page by default, of one package if given: each with latest when it is its package’s latest, image when its files are an OCI layout’s, its notes as Markdown and as html, 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 index

Lexicon

A repository's releases, of every package or of one, newest first, a page at a time: follow cursor until 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, optional
cursorstring, at most 32 bytes, optionalHow many the pages before held.

Output

releasesarray of defs#releaseView
packagesarray of defs#packageView, optionalEach package the repository publishes, by name, whatever package asked for, for a filter to name. Absent when it publishes none.
cursorstring, optionalAbsent on the last page.

Errors

NotFound

dev.knotless.listReleases.json

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 ref named 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 entry
readmereadme, optional
checksarray 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

dev.knotless.getTree.json

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 ref named 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 path is the root.

Errors

NotFound

dev.knotless.getLastCommits.json

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 ref named it, less any path.
pathstringThe file, from the root.
kindstring, image, binary, large, textHow it is shown: only text comes with its text.
sizeintegerIn bytes.
linesinteger
textstring, optional
rangesstring, optionalThe ranges its highlighting paints, encoded as the pages read them.
htmlstring, optionalA Markdown file made into HTML, as a README is.

Errors

NotFound

dev.knotless.getFile.json

GET dev.knotless.getBlame?repo=&ref=&path=

who last changed each line of a text file, as git blame finds 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 KiB

Lexicon

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 ref named 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

dev.knotless.getBlame.json

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 line

Lexicon

The commits reachable from a revision that changed a path, newest first, a page at a time: follow cursor until 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, optional
cursorstring, 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 ref named it, less any path.
pathstringThe path, from the root; empty for the whole tree.
commitsarray of defs#commitView
checksarray 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

dev.knotless.listCommits.json

GET dev.knotless.getCommit?repo=&sha=

a commit and its diff

Lexicon

A commit, and what it changed, file by file, as git show has 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#commitView
filesarray of defs#fileDiffView

Errors

NotFound

dev.knotless.getCommit.json

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 checks

Lexicon

A repository's branches, the default first and the rest by name, a page at a time: follow cursor until 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, optional
cursorstring, 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 branchView
cursorstring, optionalAbsent on the last page.

Errors

NotFound

dev.knotless.listBranches.json

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 checks

Lexicon

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 cursor until 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, optional
cursorstring, 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 tagView
cursorstring, optionalAbsent on the last page.

Errors

NotFound

dev.knotless.listTags.json

GET dev.knotless.listItems?repo=&kind=&state=&q=&limit=&cursor=

the open or closed pulls or issues that the filter q matches (collaboration.md), review-requested:@me the asker, each with draft for a draft pull and the reviewers it asks, 50 a page by default, how many of each, and the repository’s labels and the items’ authors, for a filter to name

Lexicon

A repository's pulls or its issues, open or closed, newest first, a page at a time: follow cursor until it is absent. q filters 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, issue
statestring, 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 (over state), 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, optional
cursorstring, at most 32 bytes, optionalHow many the pages before held.

Output

itemsarray of defs#itemView
openCountintegerHow many of its kind are open, of those q matches.
closedCountintegerHow many of its kind are closed or merged, of those q matches.
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

dev.knotless.listItems.json

GET dev.knotless.getThread?repo=&uri=&filesOnly=&firstRows=

a pull or an issue and everything said on it, each comment with its uri, Markdown body and, for a review, the commit it looked at; on a pull’s lines, its line (path, side, line, startLine, the lines it names as they were, at most 20, and outdated once that side of the file has changed), the review it went with and resolved; an answer’s reply, the comment starting its thread; for a pull, reviews, where each reviewer stands (outdated once the open pull has moved past the head it looked at, maintainer for the owner or a collaborator), and in changes the head it 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 its item and closes where it names it after a closing keyword; in each html, a bare at:// 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, reactions on the item and on each comment, by content in GitHub’s order, each naming the asker’s own reaction when it has an asker; and with an asker, what it may do (viewer: setState, hide, revert for a merged pull, and the labels it may set); with filesOnly, a pull’s changed files without their rows, and with firstRows too, the rows of the first files, up to 10 files or 1500 lines, passing over any that changes more than 400

Lexicon

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#itemView
bodystring, 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

dev.knotless.getThread.json

GET dev.knotless.getPullDiff?repo=&uri=&paths=

the files of a pull’s diff that paths names (repeated, 1 to 50), rows and all: what the pull page fetches as the reader comes to each file

Lexicon

The files of a pull's diff that paths names, rows and all, as dev.knotless.getThread shows them, for a page that listed them with its filesOnly. 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

dev.knotless.getPullDiff.json

GET dev.knotless.getTemplate?repo=&kind=&base=

the template a new pull (at base) or issue is asked to follow, .knotless/<kind>.md, as knotless template reads it; {} with none, 400 for a kind that is neither

Lexicon

The template a repository asks its pulls or issues to be written from, as knotless template reads it: .knotless/pull.md or .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

dev.knotless.getTemplate.json

GET dev.knotless.getCompare?repo=&base=&head=&branch=

the repositories a pull can come from (heads: this one, then its forks), and with branch what it brings in from head (this repository if not given) into base (the default branch if not given), as a pull’s changes: 404 for a branch on neither

Lexicon

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

dev.knotless.getCompare.json

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 index

Lexicon

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

dev.knotless.getReactions.json

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

dev.knotless.getQueue.json

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 cursor until 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, optional
cursorstring, at most 2048 bytes, optionalThe cursor of the page before.

Output

runsarray of runView
cursorstring, 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.

dev.knotless.listRuns.json

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#checkView
refstringThe 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

dev.knotless.getCheck.json

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

dev.knotless.getCheckImage.json

GET dev.knotless.getAvatar?actor=&cid=

a dev.knotless.profile picture

Lexicon

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

dev.knotless.getAvatar.json

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's html shows 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

dev.knotless.getAttachment.json

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: true for an operator, delegated: true when its gateway writes for the asker, organization: true when the asker is an organization, reauth: true when their login to the pages asked for less than the pages ask for now, redelegate: true when the gateway writes for them under a grant that asked for less than it does now, and spaces, 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.

dev.knotless.getViewer.json

GET dev.knotless.getFollowers?actor=&limit=&cursor=

the accounts following an account, the latest first, 50 a page by default: 404 with no index, ProfileNotFound for an unknown account

Lexicon

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, optional
cursorstring, at most 512 bytes, optionalWhere the page before ended.

Output

actorsarray of defs#actorView
cursorstring, optionalAbsent on the last page.

Errors

ProfileNotFound

dev.knotless.getFollowers.json

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, optional
cursorstring, at most 512 bytes, optionalWhere the page before ended.

Output

actorsarray of defs#actorView
cursorstring, optionalAbsent on the last page.

Errors

ProfileNotFound

dev.knotless.getFollows.json

GET dev.knotless.listThemes?actor=&q=&uris=&limit=&cursor=

dev.knotless.theme records, everyone’s or actor’s, the latest first, as {themes, cursor}; with q, everyone’s whose name starts with it in any case, by name, with no cursor; with uris, 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. ProfileNotFound for no such actor, 400 for over 50 uris, 404 with no index

Lexicon

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, optional
cursorstring, at most 512 bytes, optionalWhere the page before ended.

Output

themesarray of defs#themeView
cursorstring, optionalAbsent on the last page.

Errors

ProfileNotFound, NotFound

dev.knotless.listThemes.json

GET dev.knotless.listSteps?repo=&q=&limit=&cursor=

the shared steps the index has found (serve.md), everyone’s or one repository’s (repo, its dev.knotless.repo URI, by handle or DID), by name, 50 a page by default, those whose name starts with q if 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}, useCount how many public repositories pin it in the .knotless/uses.lock of their default branch. One taken down, or of an account or repository taken down, is left out. 400 for a repo that is not a repository’s URI, 404 for no such repository or with no index

Lexicon

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 with q. 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, optional
cursorstring, at most 512 bytes, optionalWhere the page before ended.

Output

stepsarray of defs#stepView
cursorstring, optionalAbsent on the last page.

Errors

NotFound

dev.knotless.listSteps.json

GET dev.knotless.getStep?repo=&name=

one of those, as listSteps gives it, with its latest version’s commit, its file’s path, the uses that takes its major version (<handle>/<repo>/<name>@1, or @0.3 before 1.0.0), its inputs, each {name, description, required, default}, the commands it needs and the secrets it asks for, each {name}, its versions, each {uri, version, commit, createdAt}, the highest first, and usedBy, at most 30 of the repositories pinning it. 404 for no such repository or step, or with no index

Lexicon

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#stepViewDetailed

Errors

NotFound

dev.knotless.getStep.json

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 isRead by seenAt, and a review’s verdict; reason mention for 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 with mentioned: true; reasonSubject what 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-requested for a pull, not a draft, that asks the asker to review it; reason invite for an organization’s invitation the asker has yet to answer, while it stands, with the organization as author, its dev.knotless.orgMember record of the asker as uri, the role it offers and no repo; reason checks-failed for 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 as author, a failed check as uri, the pull as reasonSubject and checks, each {workflow, status, check}; reason ejected for the asker’s pull the merge queue took out, with its runner’s dev.knotless.queue record as uri and the checks that failed as checks. A pull, an issue, a comment, a mention in one, or a review request has text, the start of what it says as Markdown, at most 1,000 characters; a review request has reviewed: true once the asker has approved the pull or asked for changes since it was asked; each about a pull or an issue has subjectState, open, closed or merged, as its page says, and checks-failed and ejected are left out once their pull has merged or closed. kind narrows them to one of mentions (with comments heard of for another reason that name the asker), reviews (review requests, and comments with a verdict), comments (those without), pulls-and-issues, checks (checks-failed and ejected) or stars-and-follows; an unknown kind answers none: 401 AuthenticationRequired with no asker, 404 with no index

Lexicon

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, optional
cursorstring, 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; reviews asked of it, and comments that review its pulls; comments that do not review; pulls-and-issues opened; checks that failed and the merge queue turning a pull away; stars-and-follows. A kind the appview does not know answers nothing.

Output

notificationsarray of notification
cursorstring, optionalAbsent on the last page.
seenAtstring (datetime), optionalWhen the account last said it had seen its notifications, if ever.

Errors

AuthenticationRequired

dev.knotless.listNotifications.json

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 100

Errors

AuthenticationRequired

dev.knotless.getUnreadCount.json

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, night the 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 #notificationPref always, which it hears of: reasons, each listNotifications reason (from its lexicon’s knownValues, so a new one is listed) with enabled, and followedOnly, whether only of what accounts it follows did. Until it says, all, from anyone. listNotifications and getUnreadCount leave out the rest: 401 with no asker, 404 with no index

Lexicon

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

preferences

Errors

AuthenticationRequired, NotFound

dev.knotless.getPreferences.json

GET dev.knotless.getOwnProfile

the asker’s dev.knotless.profile alone, as its settings show it (displayName, description, pronouns, links, avatar’s address, bot), with nothing of its Bluesky profile: 401 with no asker

Lexicon

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

ownProfile

Errors

AuthenticationRequired

dev.knotless.getOwnProfile.json

GET dev.knotless.getAccountData

what knotless data the account logged in to the pages holds, read from its PDS: {did, collections, repos, spaces}, each dev.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 otherwise

Lexicon

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-data lists 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

dev.knotless.getAccountData.json

GET dev.knotless.getEmails

{count}, how many addresses the asker’s dev.knotless.emails claims: only hashes are kept, so they cannot be listed. 401 with no asker

Lexicon

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

dev.knotless.getEmails.json

GET dev.knotless.listBlocks

the asker’s blocks, the latest first, from its own repo: each uri, subject (an actor), repo when it is from one repository only, and createdAt. Those naming a private repository live in its space and are not listed. 401 with no asker

Lexicon

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 block

Errors

AuthenticationRequired

dev.knotless.listBlocks.json

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, as knotless storage counts 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

dev.knotless.getRepoSettings.json

GET dev.knotless.getOrg?actor=

an organization’s members, as the index has its dev.knotless.org and dev.knotless.orgMember records and its members’ dev.knotless.orgAcceptance ones: {org, organization, inside, gateway?, members}, gateway the one its public dev.knotless.org record names, each member {actor, role, standing, invitedAt, expiresAt?, joinedAt?}, the first invited first, standing one of invited, lapsed, public and hidden. 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; ProfileNotFound for an unknown account

Lexicon

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#actorView
organizationbooleanWhether 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

dev.knotless.getOrg.json

GET dev.knotless.getActorStars?actor=

the public repositories an account starred, newest first, each with its language, starCount and forkCount: 404 with no index, ProfileNotFound for an unknown account

Lexicon

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#repoView

Errors

ProfileNotFound

dev.knotless.getActorStars.json

GET dev.knotless.getMergeStatus?repo=&pull=

whether an open pull can merge now, as knotless merge --check says (below): 401 with no asker, 400 for a pull not open, 404 for none

Lexicon

Whether an open pull can merge now, decided as knotless merge decides 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.
maypermissions
queueBlockedstring, 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 --check lists 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

dev.knotless.getMergeStatus.json

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 ref

Lexicon

The workflows under .knotless/workflows at a ref of a repository, in file name order, each with what runs it there, as knotless workflows lists 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 workflowView

Errors

NotFound

dev.knotless.listWorkflows.json

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), as keys, {runner, key}

Lexicon

The accounts a repository names in its record as its runners, as knotless runners lists 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

runners

Errors

NotFound

dev.knotless.listRunners.json

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.

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 a knotless_login cookie for 15 minutes; 400 No 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-pds names, 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 /), setting knotless_session for 90 days if approved, and keeping the account it replaces, if any, in knotless_accounts; 400 for a login this browser did not start

POST /oauth/switch

{did}: makes that account, one knotless_accounts holds, the one in use, keeping the one before in its place; 400 This 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#starState

Errors

NotFound, AuthenticationRequired

dev.knotless.starRepo.json

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#starState

Errors

NotFound, AuthenticationRequired

dev.knotless.unstarRepo.json

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 a seenAt that is no datetime

Lexicon

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

dev.knotless.updateSeen.json

POST /xrpc/dev.knotless.followActor

{subject}, a DID: follows it as the account logged in, answering {followersCount, following}; 400 for the account itself, ProfileNotFound for no account

Lexicon

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#followState

Errors

ProfileNotFound, AuthenticationRequired

dev.knotless.followActor.json

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#followState

Errors

ProfileNotFound, AuthenticationRequired

dev.knotless.unfollowActor.json

POST /xrpc/dev.knotless.createTheme

{name, palette}: writes a dev.knotless.theme as the account logged in, and indexes it, answering it as listThemes does; 400 for a name empty or over 64 characters, a scheme not light or dark, a colour not #rrggbb, none of background, text, primary and accent, or a text that would not read at 4.5 to 1 on what it is drawn on, each named, as textMuted on background is 1.2 to 1

Lexicon

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#palette

Output

defs#themeView

Errors

InvalidRequest, AuthenticationRequired

dev.knotless.createTheme.json

POST /xrpc/dev.knotless.deleteTheme

{uri}: deletes one of the account’s own themes, answering {}; 400 for one that is not its own

Lexicon

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

dev.knotless.deleteTheme.json

POST /xrpc/dev.knotless.updateProfile

{displayName, description, pronouns, links, removeAvatar, bot}, each optional: changes the account’s dev.knotless.profile as knotless profile does, a field given empty taken out, bot true saying the account is a bot and false that it is not, answering what it holds now, as getOwnProfile; 400 for a field too long or a link that is no http or https address

Lexicon

Changes the dev.knotless.profile record of the account logged in to this appview's web pages, as knotless profile does: 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#ownProfile

Errors

InvalidRequest, AuthenticationRequired

dev.knotless.updateProfile.json

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 kind

Lexicon

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 --avatar does: 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#ownProfile

Errors

InvalidRequest, AuthenticationRequired

dev.knotless.uploadAvatar.json

POST /xrpc/dev.knotless.addEmail

{email}: claims it, as knotless 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 add does, 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

email

Output

emailState

Errors

InvalidRequest, AuthenticationRequired

dev.knotless.addEmail.json

POST /xrpc/dev.knotless.removeEmail

{email}: takes the claim back, answering the same

Lexicon

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 remove does. Requires the session cookie the pages' login sets: the appview writes with the session it keeps for that account.

Input

addEmail#email

Output

addEmail#emailState

Errors

InvalidRequest, AuthenticationRequired

dev.knotless.removeEmail.json

POST /xrpc/dev.knotless.blockActor

{subject, repo}, a handle or DID, and optionally the at:// 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’s

Lexicon

Writes a dev.knotless.block record in the account logged in to this appview's web pages, as knotless block does, 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

block

Output

blockState

Errors

InvalidRequest, NotFound, AuthenticationRequired

dev.knotless.blockActor.json

POST /xrpc/dev.knotless.unblockActor

{subject, repo}: takes those blocks back, answering the same

Lexicon

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 unblock does. Requires the session cookie the pages' login sets: the appview writes with the session it keeps for that account.

Input

blockActor#block

Output

blockActor#blockState

Errors

InvalidRequest, NotFound, AuthenticationRequired

dev.knotless.unblockActor.json

POST /xrpc/dev.knotless.putPreferences

{preferences}: each takes the place of its kind whole, and the rest stay, answering all of them as getPreferences. A #notificationPref’s reasons not named are on, and followedOnly absent is false; an #appearancePref with no theme follows the system, and logging in to the pages copies its theme to the browser’s theme cookie, and for custom, its palette to the palette cookie, and its night’s to theme-night and palette-night. A #themesPref holds 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 in a-z, 0-9 and - (64 at most), custom with no palette, a palette not as its lexicon says (its contrast is not checked here), a source that is no dev.knotless.theme, or an added theme whose uri is no dev.knotless.theme, whose palette is not as its lexicon says, whose name is over 64 characters, whose creator is no DID or whose addedAt is no time

Lexicon

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#preferences

Errors

InvalidRequest, AuthenticationRequired, NotFound

dev.knotless.putPreferences.json

POST /xrpc/dev.knotless.createRepo

{name, description?, defaultBranch?, private?}: creates a repository in the account logged in, public or with private a space of its own, as knotless 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 spaces

Lexicon

Writes a dev.knotless.repo record under the name given in the account logged in to this appview's web pages, as knotless create does: 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 --private does: a space of its own on the account's PDS, which only its members read, its runners among them. Public when absent.

Output

defs#repoMade

Errors

InvalidRequest, AuthenticationRequired

dev.knotless.createRepo.json

POST /xrpc/dev.knotless.updateRepo

{repo, description?, links?, topics?, defaultBranch?}: as knotless 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 twenty

Lexicon

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 describe does. 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

dev.knotless.updateRepo.json

POST /xrpc/dev.knotless.renameRepo

{repo, name}: as knotless rename, answering {uri, name}

Lexicon

Moves the repository's dev.knotless.repo record to the new name, as knotless rename does, 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#repoMade

Errors

NotFound, InvalidRequest, AuthenticationRequired, Forbidden

dev.knotless.renameRepo.json

POST /xrpc/dev.knotless.compactRepo

{repo}: as knotless 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 compact does, 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

dev.knotless.compactRepo.json

POST /xrpc/dev.knotless.transferRepo

{repo, to?, name?}: offers it to the account to under name (its own if absent), or with no to withdraws the offer, as knotless transfer, answering {transfer}, where it is offered now

Lexicon

Names, in the repository's dev.knotless.repo record, the account and name it is offered to, as knotless transfer does, or with no to takes 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

dev.knotless.transferRepo.json

POST /xrpc/dev.knotless.archiveRepo

{repo, unarchive?}: archives it, making it read only, or with unarchive makes it writable again, as knotless archive, answering {archivedAt} while it is archived; one already as asked is 400

Lexicon

Sets or drops archivedAt on the repository's dev.knotless.repo record, as knotless archive does. 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

dev.knotless.archiveRepo.json

POST /xrpc/dev.knotless.acceptTransfer

{repo}: as knotless accept, by the account it is offered to, answering {uri, name} where it is now

Lexicon

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 accept does: 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#repoMade

Errors

NotFound, InvalidRequest, AuthenticationRequired

dev.knotless.acceptTransfer.json

POST /xrpc/dev.knotless.deleteRepo

{repo}: as knotless 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 delete does; 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

dev.knotless.deleteRepo.json

POST /xrpc/dev.knotless.deleteAccountData

{did, limit?}, did the account asking: with the pages’ cookie, deletes up to limit (400 by default) of its knotless records and spaces with the session kept for it, as knotless delete-data does, answering {deleted, done}; asked again, it carries on. Once done, 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 400 RecordsRemain. 403 Forbidden for a did not the asker’s

Lexicon

Deletes the asking account's knotless data a part at a time, as knotless delete-data does: 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 to limit records 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

dev.knotless.deleteAccountData.json

POST /xrpc/dev.knotless.createBranch

{repo, name, from?}: as knotless branch, answering {name, sha}

Lexicon

Points a new branch at a commit or branch, the default branch when none is named, as knotless branch does. 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

dev.knotless.createBranch.json

POST /xrpc/dev.knotless.deleteBranch

{repo, name}: as knotless branch --delete, answering {name}

Lexicon

Deletes a branch, never the default one, as knotless branch --delete does. 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

dev.knotless.deleteBranch.json

POST /xrpc/dev.knotless.renameBranch

{repo, name, to}: as knotless branch --rename, answering {name}, the new one

Lexicon

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

dev.knotless.renameBranch.json

POST /xrpc/dev.knotless.protectBranch

{repo, branch, merges?, queue?, reviews?: {approvals?, codeOwners?, dismissStale?}}: as knotless 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 protect does: 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

dev.knotless.protectBranch.json

POST /xrpc/dev.knotless.unprotectBranch

{repo, branch}: as knotless unprotect, answering {rules}

Lexicon

Takes the branch out of the protected branches of the repository's dev.knotless.repo record, as knotless unprotect does. 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

dev.knotless.unprotectBranch.json

POST /xrpc/dev.knotless.defineLabel

{repo, name, color?, description?, exclusive?}: as knotless 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 add does. 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

dev.knotless.defineLabel.json

POST /xrpc/dev.knotless.removeLabel

{repo, name}: as knotless labels remove, answering {labels}

Lexicon

Takes the label out of the repository's dev.knotless.repo record, as knotless labels remove does: 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

dev.knotless.removeLabel.json

POST /xrpc/dev.knotless.shareRepo

{repo, with, write?}: as knotless share --write, or with write: false as knotless share, letting them only read a private repository, answering {collaborators}; 400 to share for writing before delegating, or a public one for reading

Lexicon

Names the account among the collaborators of the repository's dev.knotless.repo record, as knotless share --write does: it pushes and commits through the gateway the owner delegated writes to, which must be named first, as knotless delegate names 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 --write lets it; false lets it only read a private repository, and open pulls and issues there, as knotless share does, 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

dev.knotless.shareRepo.json

POST /xrpc/dev.knotless.delegateWrites

{}: starts delegating the account’s writes to this server’s gateway, as knotless delegate does, answering {url, state}, the page where the account approves the gateway’s grant

Lexicon

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

dev.knotless.delegateWrites.json

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 started

Lexicon

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

dev.knotless.awaitDelegation.json

POST /xrpc/dev.knotless.inviteToOrg

{org, member, role?}, member a DID or handle: as knotless 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 delegating

Lexicon

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

dev.knotless.inviteToOrg.json

POST /xrpc/dev.knotless.removeFromOrg

{org, member}: as knotless 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

dev.knotless.removeFromOrg.json

POST /xrpc/dev.knotless.setOrgMembership

{org, visibility}, public or hidden: as knotless 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

dev.knotless.setOrgMembership.json

POST /xrpc/dev.knotless.endOrgMembership

{org}: as knotless org leave, leaving or declining an invitation, answering {warning?}, set when the gateway did not take them out

Lexicon

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

dev.knotless.endOrgMembership.json

POST /xrpc/dev.knotless.deleteOrg

{}: as knotless 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

dev.knotless.deleteOrg.json

POST /xrpc/dev.knotless.unshareRepo

{repo, with}: as knotless unshare, answering {collaborators}

Lexicon

Takes the account out of the collaborators of the repository's dev.knotless.repo record, as knotless unshare does, 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

dev.knotless.unshareRepo.json

POST /xrpc/dev.knotless.setTriager

{repo, who, scopes}: as knotless 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 add does: 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

dev.knotless.setTriager.json

POST /xrpc/dev.knotless.removeTriager

{repo, who}: as knotless triagers remove, answering {triagers}

Lexicon

Takes the account out of the triagers of the repository's dev.knotless.repo record, as knotless triagers remove does. 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

dev.knotless.removeTriager.json

POST /xrpc/dev.knotless.forkRepo

{repo, name?}: forks it into the account logged in, under its own name or name, as knotless fork does, answering {uri, name}; 404 for no such repository, 400 for a name the account has already

Lexicon

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 fork does. 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#repoMade

Errors

NotFound, InvalidRequest, AuthenticationRequired

dev.knotless.forkRepo.json

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 it

Lexicon

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 --image does: 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's images, with a body linking to url. 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, and height, with the blob in images.
heightinteger, optionalHow high it is drawn, in CSS pixels.

Errors

NotFound, InvalidRequest, AuthenticationRequired

dev.knotless.uploadImage.json

POST /xrpc/dev.knotless.createIssue

{repo, title, body?, images?, labels?}: opens an issue as the account logged in, answering {uri}, with labels (each {name}) put on as labelItem would; 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 on

Lexicon

Writes a dev.knotless.issue record in the account logged in to this appview's web pages, as knotless issue does, 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 bytes
bodystring, 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

dev.knotless.createIssue.json

POST /xrpc/dev.knotless.createPull

{repo, base, head, branch, title, body?, images?, draft?, labels?, reviewers?}: opens a pull to merge branch of head, this repository or a fork, into base, as a draft with draft, answering {uri}, with labels put on as createIssue does and reviewers (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 on

Lexicon

Writes a dev.knotless.pull record in the account logged in to this appview's web pages, as knotless pull does: 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 bytes
bodystring, 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; requestedAt is 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

dev.knotless.createPull.json

POST /xrpc/dev.knotless.createComment

{repo, subject, body, verdict?, images?, lines?}: comments on a pull or issue as the account logged in, or with verdict (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 as subject answers it in its thread. Answers the first it wrote’s {uri}; 400 for a comment that says nothing

Lexicon

Writes a dev.knotless.comment record in the account logged in to this appview's web pages, as knotless comment does: 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, or changes to 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

dev.knotless.createComment.json

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 with false marks it ready. images, each {image, width?, height?} with a blob and size uploadImage gave, are attached to what the body links to (collaboration.md). It, createIssue, createPull and createComment write each @handle of 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 edit does: 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

dev.knotless.editItem.json

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-comment does. 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

dev.knotless.deleteComment.json

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, resolve and unresolve do. 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, or open again; for a thread, resolved, or open again. Merging a pull is another thing.

Output

uristring (at-uri)The dev.knotless.state record written.

Errors

NotFound, AuthenticationRequired

dev.knotless.setItemState.json

POST /xrpc/dev.knotless.labelItem

{repo, subject, label, removed?}: puts a label on, or takes it off, answering the {labels} it carries now

Lexicon

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 label and knotless unlabel do: 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

dev.knotless.labelItem.json

POST /xrpc/dev.knotless.hideItem

{repo, subject, hidden?}: as the owner, hides a pull, issue or comment, or with hidden: false shows 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 hide does; or, with hidden false, deletes those there are, as knotless unhide does. 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

dev.knotless.hideItem.json

POST /xrpc/dev.knotless.renderMarkdown

{text, repo?}: the HTML the pages show text as once posted, relative links inside the repository repo names and each @handle a link to its profile, answering {html}; needs no login, and 400 for text past 20000 bytes, a body’s longest

Lexicon

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.

dev.knotless.renderMarkdown.json

POST /xrpc/dev.knotless.createReport

{subject, reason, text?}: reports a handle, knotless:// URL or at:// 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 report does. 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

dev.knotless.createReport.json

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, 404 NotFound for a subject the pages do not show

Lexicon

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#reactionContent

Output

defs#reactionState

Errors

NotFound, AuthenticationRequired

dev.knotless.addReaction.json

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#reactionContent

Output

defs#reactionState

Errors

NotFound, AuthenticationRequired

dev.knotless.removeReaction.json

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.

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.pngsocial 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.