Glossary

For anyone who has met a term in these guides, in the Scaladoc, or in Forgejo's own vocabulary and wants a short definition. Terms are grouped by where they come from rather than alphabetised, because the confusions are between neighbours.

Identifiers

Owner

The user or organisation that owns a repository — the first segment of owner/name. Answers "who does this repository belong to?", and the answer may be either kind of account.

Owner.from("forgejo") returns Either[ValidationError, Owner]. It rejects a blank value, a /, and control characters, because an Owner is interpolated into a request path.

Username

The handle that names a person — the {username} of /users/{username}. Answers "which account is this?".

Deliberately not the same type as Owner, even though the two are the same characters on the wire. The endpoints that take a Username/users/{u}/followers, /users/{u}/keys — are about a person, and one shared type would let client.users.keys(repository.slug.owner) compile against an organisation that has no keys. Converting is a deliberate step through Username.from, never an implicit widening.

OrgName

The handle that names an organisation — the {org} of /orgs/{org}. Not Owner and not Username, for the same reason: one shared type would let client.organizations.members(repository.slug.owner) compile against a personal account, which has no members and answers 404.

So there are three types for what is, on the wire, one string. That is the point. Each one says what kind of question the endpoint is asking.

Slug

The owner/name pair that identifies a repository, as Forgejo displays it and as the API paths spell it: forgejo/forgejo.

Modelled as RepoSlug(owner, name) with a value that renders the canonical form. It is a real type because two loose strings let a caller swap them silently, and every repository endpoint takes both.

Elsewhere in web software "slug" often means a URL-safe version of a title; that is not the meaning here.

RepoName

The second segment of owner/name, on its own. Validated the same way as Owner.

Git terms

Ref

Short for reference: a name that points at a commit. refs/heads/main, refs/tags/v1.2.0. Branches and tags are both refs; a ref is the general case.

RefName covers the whole family — whole or partial, refs/heads/main or heads/main or main on its own — because Forgejo's raw-Git endpoints spell a ref in three different positions and one type serves all three.

Branch

A ref under refs/heads/ that moves as commits are added. BranchName is the type.

A branch name may contain /. renovate/some-dependency-0.x and v16.0/forgejo are both real names from the captured fixtures. That matters because Forgejo routes them with a wildcard, so the slashes have to reach the wire as real separators: a name percent-encoded whole into one path segment answers 404. BranchName.segments exists for exactly that, and it is what the request builder uses.

Tag

A ref under refs/tags/ that does not move. TagName is the type.

An annotated tag is a Git object in its own right, carrying a message and a tagger; a lightweight tag is only a name pointing at a commit. client.repos.git.getAnnotatedTag reads the first kind.

Sha / blob id

A Git object identifier — 40 hex characters for SHA-1, 64 for SHA-256. CommitSha is the type, and it accepts 4 to 64 characters so that abbreviated ids work.

The word does double duty in this API. On a file write, UpdateFile.expectedSha is the blob id of the file's current contents, not a commit id. Both are CommitSha because both are Git object ids of the same shape.

Pagination

Page and limit

Forgejo's two query parameters. page is the one-based index of the window you want; limit is how many items it may hold. This library sends both, always, as one PageParams(page, size) value.

Never send a lone limit: list endpoints given one have been observed to ignore it and return the entire collection.

PageSize

The validated form of limit. Accepts 1..50 and rejects anything larger rather than clamping it. PageSize.Default is 30.

PageNumber

The validated form of page. One-based, and 0 is rejected — Forgejo silently treats page=0 as page=1, which would hide an off-by-one in your code.

Page[A]

What one request returns: the items, the params that produced them, an optional totalCount, and nextPage / prevPage. It is never the whole collection.

Clamp

Forgejo reducing your limit to the instance's own max_response_items without saying so, while the Link header still echoes what you asked for. The reason items.size is not an end-of-pages test. See Pagination.

RFC 5988. A comma-separated list of URIs with relation types — rel="next", rel="prev", rel="first", rel="last". The presence of rel="next" is the only sound end-of-collection test against Forgejo.

This library's own vocabulary

Rail

One of the two ways every operation is exposed.

The convenience rail is the method on the group: client.repos.get(...) returns Future[Repository] and fails the Future with CodebergException.

The typed rail is the same operation under .attempt: client.repos.attempt.get(...) returns Future[Either[CodebergError, Repository]] and never fails.

They are one implementation. .attempt is the convenience rail with its failure channel materialised, so the two cannot drift. Pick one per call site; see Errors.

Command

A value describing a write, built by naming what should be set, rather than a method with a row of optional parameters: CreateIssue, EditIssue, MergePullRequest, UpdateFile. Only what you set is sent.

CallContext

Attached to every remote failure: the operation id, the HTTP method, the redacted URI, the instance's request id when it sent one, and how long the attempt took. It is what lets you tell which call failed without correlating logs.

Operation id

A stable, greppable string identifying one endpoint — "repos.get", "issues.list", "version.get". It never changes, so it is safe to build alerts and metrics on. It appears in CallContext.operation and as a val on each API companion object.

Redaction

Removing credential material before a value can be logged. Here it is a guarantee with tests behind it: ApiToken and Password render as *** from toString and from string interpolation; the URI in a CallContext is redacted by the transport before the context is built; CodebergError.describe is assembled only from that redacted context and from server-supplied text.

RetryEligibility

Whether an operation may be repeated at all: Never, IdempotentOnly (safe methods only), or AlwaysRetry. Stated by the operation, not by the caller — Forgejo has no idempotency keys, so it cannot be inferred from the response.

Telemetry

The observation port. This library has no logging dependency and writes nothing anywhere; you implement Telemetry[Future] and pass it at construction. See Observability.

Golden fixture

A response captured verbatim from a real instance and checked into the repository, used to build and test the models. Where the specification and a fixture disagree, the fixture wins — the specification declares no required fields on any response model and no nullable anywhere, so read literally it asserts that every field of every response is optional and none may be null.

HTTP and general terms

Safe method

RFC 9110's term: a method with no intended side effect on the server. GET and HEAD. Only safe methods are retried without the operation opting in.

Idempotent

A request that produces the same server state whether it is made once or many times. Deleting a numbered row is idempotent; creating an issue is not.

The word is often used loosely as "safe to retry", and this library is careful not to: RetryEligibility.IdempotentOnly means "safe methods only", and the stronger claim — that a mutating call may be repeated — is AlwaysRetry, and is argued for per operation.

Optimistic concurrency

Making a write conditional on the state you read, so that a concurrent change is detected rather than overwritten. Here it is UpdateFile.expectedSha (Forgejo answers 409 if the file moved on) and MergePullRequest.expecting (Forgejo refuses if the branch head moved).

Scala terms

Future[A]

A value that is not there yet and will eventually hold an A or hold a failure. Scala's equivalent of a JavaScript Promise or a Java CompletableFuture.

ExecutionContext

Where the continuations of a Future run — a thread pool, essentially. Scala makes you name one rather than picking a global default, which is why you write given ExecutionContext = … once per scope. See Getting started.

given / using

Scala 3's context parameters. A using parameter is one the compiler supplies from a matching given value in scope, instead of your passing it at every call site. It is how ExecutionContext travels.

Opaque type

A type that is a distinct type at compile time and its underlying representation at run time. opaque type Owner = String means an Owner is a String when the program runs — no wrapper object, no allocation — but a String cannot be passed where an Owner is expected, and the only way to make one is the smart constructor.

That is why most identifiers here are opaque types. The two credential types — ApiToken and Password — are ordinary final classes instead, because an opaque alias over String cannot stop toString from printing the secret outside its own file.

Smart constructor

A function that validates its input and returns the type or an error, instead of a public constructor that trusts you. Here they are called from (Owner.from, ApiToken.from, PageSize.from) or of (CreateIssue.of, MergePullRequest.using), and they return Either[ValidationError, A] when they can fail.

ADT

Algebraic data type — a type defined as a closed set of alternatives, each with its own fields. Scala 3 spells it enum. CodebergError is one, with five cases; so are Auth, TransportCause, ContentEntry and FileOperation.

The value of a closed set is that the compiler can tell you when a match has forgotten a case.

Port

A trait the library defines and something else implements, so that the library does not depend on the something else. Telemetry is the one you are expected to implement; HttpPort and Decode are internal ones, satisfied by the sttp and jsoniter-scala adapters.