runlot
ReferenceAPI

Schemas

Describes the structure of API request bodies and responses. Each name links to the body entry of an API operation.

RepoRefs

FieldTypeRequiredDescription
defaultBranchstringYesThe HEAD on disk is authoritative. Repo.defaultBranch is a copy of it, and when they disagree this value is the right one (0032_repos.sql).
refsobject[]Yes

RepoTreeEntry

FieldTypeRequiredDescription
namestringYes
pathstringYesPath relative to the repository root
kindstringYes
sizeintegerYesFiles only; 0 for directories
oidstringYes

RepoTree

FieldTypeRequiredDescription
refstringYes
commitstringYes
pathstringYes
entriesRepoTreeEntry[]Yes
truncatedbooleanYesCut off at 1000 entries

RepoBlob

FieldTypeRequiredDescription
refstringYes
commitstringYes
pathstringYes
oidstringYes
sizeintegerYes
binarybooleanYesIt contains NUL or is not UTF-8. The contents are empty
tooLargebooleanYesOver 1 MiB, so the contents were not included
contentstringYes

RepoCommit

FieldTypeRequiredDescription
oidstringYes
messagestringYes
authorstringYes
emailstringYes
timestampstringYes

RepoName

It becomes a URL path segment, so it cannot contain .. or a slash. The authoritative rule is the CHECK in migration 0032 (if validation lives only in the application, maintenance scripts bypass it), and what is here is a copy of it. It cannot begin with a dot. It is wider than a project name (a hostname label) — uppercase letters, dots and underscores are allowed.

Type: string

RepoVisibility

Defaulting to private is deliberate — if the default were open, the direction of a mistake would be a leak. public opens anonymous clone (§5.1).

Type: string

TokenScope

A token inherits the user's permissions but can only lower them (§5.2). There is no arithmetic in the upward direction.

Type: string

Repo

FieldTypeRequiredDescription
repoIdstringYes
orgSlugSlugYes
projectSlugYesThe project this repository belongs to (§19). The name is unique only inside it.
nameRepoNameYes
visibilityRepoVisibilityYes
defaultBranchstringYesThe HEAD on disk is authoritative and this value is a copy of it (0032_repos.sql). When they disagree the disk wins — node-git's maintenance pushes it back.
sizeBytesintegerYesAn approximation for quota enforcement. The exact value is on disk (§8.2).
lastPushedAtstringNo
createdAtstringYes
cloneUrlstringNohttps://<git-domain>/<org>/<project>/<repo>.git. CP builds it — if the CLI assembled it, the local shape would be stamped onto production (docs/dogfood-friction.md ⑤). An empty string when --git-domain is empty.
sshUrlstringNogit@<git-ssh-host>:<org>/<project>/<repo>.git. The name is the same as cloneUrl (§18). An empty string when --git-ssh-host is empty.

RefUpdate

FieldTypeRequiredDescription
refstringYes
oldstringNonull means a creation. It is the only clue for getting back a commit lost to a force-push.
newstringNonull means a deletion
forcedbooleanYesIt was a non-fast-forward. The MVP has no protected branch, so it goes into the record instead of being forbidden (§5.1).
actorUserstringNo
createdAtstringYes

SSHKey

FieldTypeRequiredDescription
keyIdstringYes
namestringYes
keyTypestringYes
fingerprintstringYesGlobally UNIQUE, because the key is the identity (§5.3).
createdAtstringNo
lastUsedAtstringNo

AccessToken

FieldTypeRequiredDescription
tokenIdstringYesIt is carried in the token string in plaintext (runlot_pat_<id>_<secret>). That is why the lookup is one index hit and the comparison is constant time (§5.2).
namestringYes
scopeTokenScopeYes
createdAtstringYes
expiresAtstringNo
lastUsedAtstringNo
revokedAtstringNo

AccessTokenCreated

FieldTypeRequiredDescription
tokenIdstringYes
namestringYes
scopeTokenScopeYes
tokenstringYesPlaintext. It appears in this response only once. The server stores only the SHA-256 hash, so it cannot be produced again.
expiresAtstringNo

Slug

It becomes one label of a hostname, so it follows the DNS rules. The project name goes into <name>.lvh.me verbatim, so without this check you could create a project that can never be reached.

Type: string

Error

FieldTypeRequiredDescription
errorobjectYes

Session

FieldTypeRequiredDescription
tokenstringYes
expiresAtstringNo
userUserYes

User

FieldTypeRequiredDescription
userIdstringYes
providerstringYes
subjectstringYes
emailobjectNo
nameobjectNo

Org

FieldTypeRequiredDescription
orgIdstringYes
slugSlugYes
namestringYes
personalbooleanYes

OrgMembership

Type: Org & object

Notification

FieldTypeRequiredDescription
idintegerYes
kindstringYesThe kind of event (docs/limits.md §3.1). It is the value machines branch on; the sentence a person reads is composed separately by the dashboard and by the email template.
projectNamestringNoThe project name, if the notification is about a project. Absent for organization notifications (project_limit)
keystringYesThe deduplication key per kind. Only one row is created per (kind, key) in 24 hours
detailobjectYesA few numbers that differ per kind. project_limit carries {limit, count}, usage notifications carry {bytes, capBytes}, and operation notifications carry {opId, opKind, reason?}.
createdAtstringYes
sentAtstringNoThe time the email was sent. If absent, it is still queued or there was no address to receive it

Project

FieldTypeRequiredDescription
projectIdstringYes
orgSlugSlugYes
nameSlugYes
hostnamesstring[]Yes
urlsstring[]Nohostnames turned into openable addresses. The scheme and port depend on the deployment (https://… in production, http://…:8787 locally), so the server builds them — if the client assembled them, the local shape would end up printed in production.
createdAtstringYes

ProjectDetail

Type: Project & object

Placement

FieldTypeRequiredDescription
statestringYes
epochintegerYes
homeNodeobjectNo
suspendedbooleanYesSeparate from the lifecycle state. A suspended project can still have state active, and lifting the suspension returns it to that state.

ProjectOverview

The project home screen (getProjectOverview).

FieldTypeRequiredDescription
liveOverviewDeployment | nullYesThe deploy serving right now. It is null before the first deploy.
deploymentsOverviewDeployment[]YesRecent deploys, newest first. At most 5 — for all of them, listDeployments.
deploymentCountintegerYesThe total number of deploys for this project.
databaseOverviewDatabaseYes
featuresOverviewFeaturesYes
customHostnamesintegerYesThe number of custom domain rows. Rows still being verified are counted too.
reposintegerYesThe number of repositories in this project.
notificationsNotification[]YesRecent notifications for this project, newest first. At most 5.

OverviewDeployment

A deploy row in the overview. It has the same shape as Deployment (including actor).

Type: Deployment

OverviewDatabase

FieldTypeRequiredDescription
grantedbooleanYesWhether it was turned on with "database": true
lastBackupAtstringNoThe time of the last generation. Absent when there is no generation.
lastCheckedAtstringNoThe last heartbeat of the no-change skip (DatabaseGenerations.lastCheckedAt). Even when the generation count does not grow, the backup is alive as long as this value moves.
generationsintegerYesNumber of generations

OverviewFeatures

Whether the resources turned on by declaration have been granted (docs/provisioning.md). The "next step" on the home screen uses this to pick what has not been turned on.

FieldTypeRequiredDescription
databasebooleanYes
storagebooleanYes
emailbooleanYes
authbooleanYes

Deployment

FieldTypeRequiredDescription
deploymentIdstringYes
versionintegerYes
artifactShastringYesSHA-256 of the deploy bundle tarball (hex)
createdAtstringYes
createdByobjectNo
livebooleanYes
actorstringNoThe display name of the person who deployed (name → email → provider subject, in that order). createdBy is a uuid, so the screen cannot turn it into a person. It appears only in lists and the overview, and it is empty when there is no user row.
manifestManifestNoThe manifest exactly as it passed validation. Deploys from before 0040 do not have it.
bundleBytesintegerNoThe bundle size after unpacking, not the compressed size.
bundleFilesintegerNoThe number of bundle entries (files + directories).
gitCommitstringNoThe HEAD hash sent by the CLI. For display only — CP does not verify it.
gitMessagestringNoThe first line of the commit message, up to 200 characters.
gitBranchstringNo
gitDirtybooleanNoWhether the deploy carried uncommitted changes. When true, gitCommit is not the code.

DatabaseEndpoint

FieldTypeRequiredDescription
hoststringYes
portintegerYes
databasestringYes
sslmodestringYes

DatabaseTokenMeta

FieldTypeRequiredDescription
userstringYes
expiresAtstringYes
createdAtstringYes

DatabaseToken

FieldTypeRequiredDescription
userstringYes
passwordstringYesIncluded in this response only.
expiresAtstringYes
connectConnectInfoYesThe same shape as DatabaseConnect.connect, where user/password are the values of this token.

ConnectInfo

FieldTypeRequiredDescription
hoststringYesThe hostname of the project. It is not a UUID because the front decides placement from SNI (docs/actor-placement-routing.md §8).
portintegerYesThe wire listener port of node-front. It is not a value the CP computes; the operator tells both the front and the CP — the default is defined by the DefaultFrontWirePort constant in internal/slots.
databasestringYesThe project UUID. The front overwrites the database from startup with the UUID from the pin, so this is the source of truth.
userstringYesThe bootstrap role. It is separate per database, so names do not collide
passwordstringYes
sslmodestringYesValues that turn it off get no answer. The front cuts the connection at the SSLRequest rejection step, so disable never connects in the first place.

DatabaseConnect

FieldTypeRequiredDescription
dbbooleanYes
keyVersionintegerNoThe version of this project's DEK (docs/phase5.md C2). It is included in the database-creation response only, and if the key could not be provisioned the field is absent altogether. It does not answer 0, because keyVersion=0 already means "plaintext generation". Marking a provisioning failure with that value would let a user believe a plaintext backup had been settled on. If it is absent, the backup scheduler provisions the key later.
connectConnectInfoYes

DatabaseExport

FieldTypeRequiredDescription
projectIdstringYes
epochintegerYesThe incarnation that produced the image. Even within one project, a different epoch is a different database, so this value matters as much as the key.
nodestringYesThe node that took the image
exportobjectYes

DatabaseGenerations

FieldTypeRequiredDescription
projectIdstringYes
epochintegerYesThe incarnation running now. Rows in the list may belong to older epochs
statestringYesAssignment state (active·cold·restoring…)
lastCheckedAtstringNoThe last heartbeat of the no-change skip. If it is empty the check has never run. It is the value that separates "no new generations" from "the backup is dead", so it cannot be dropped from the list (docs/actor-placement-routing.md §4.6-1).
generationsGenerationRow[]YesNewest first. An empty list is an array, not null

GenerationRow

One backup generation. The field names are the same as GenerationRow in internal/backupproto. The actor metadata, the generation the node reports to the CP and the record carried by this API must use the same names, so that editing one of them does not make another silently read 0.

FieldTypeRequiredDescription
epochintegerYes
seqintegerYesMonotonically increasing within an epoch. A restore names a generation by (epoch, seq)
keystringYesOffsite key (gen/<project>/<epoch>/<stamp>-<seq>.feather)
bytesintegerYesThe size of the object stored offsite. An encrypted generation is larger than plaintext, and the admission reservation is computed from this value as well.
sha256stringYes
plainBytesintegerNoThe size of the plaintext (the feather image) inside the encrypted data. For a plaintext generation it equals bytes
keyVersionintegerNoThe DEK version used for encryption. 0 means plaintext (a generation created without a DEK)
txnIdintegerNoThe commit number the image holds. 0 means unknown
kindstringNoWhy the generation was created. Not used for decisions
createdAtstringYes
nodestringNoThe node that took the generation
latestbooleanYesThe generation gen/<project>/latest.json points at. It is conditionally monotonic, so a late generation from an older epoch cannot take this marker (docs/phase5.md A1).
finalbooleanYesA safety copy taken right before an eviction, a delete or a restore. It lives in the same table as scheduled generations, but pruning does not count it.

RestoreRequest

FieldTypeRequiredDescription
epochintegerYes
seqintegerYes

DeleteRequest

FieldTypeRequiredDescription
confirmstringYesMust match the project name exactly. The CLI prompt alone is not enough — a script that passes --yes and runs in the wrong directory never shows a prompt at all.

OperationStarted

The answer means the multi-step operation was queued (202). It does not mean it finished — follow the progress at …/database/operations/{opId}.

FieldTypeRequiredDescription
projectIdstringYes
opIdstringYes
kindstringYes
epochintegerYes
nodestringNo

Operation

FieldTypeRequiredDescription
opIdstringYes
kindstringYes
projectIdstringYes
phasestringYesThe step currently in progress (draining·sealing·committing…). The CLI prints a line only when this value changes — printing on every poll would show the polling interval instead of the progress.
attemptintegerNo
deferralsintegerNo
ownerstringNo
terminalCodestringNoWhy it ended. aborted means a person stopped it; any other value is a failure. Empty together with done means success.
failedAtstringNo
errorstringNo
donebooleanYesIt will not progress any further. terminalCode is what separates success, failure and abort
retryAfterSecondsintegerNoSeconds remaining until the next attempt. 0 means it is due now. Once a backoff is applied, "in progress" can mean two different states, so this value tells them apart.

Manifest

The runlot.json inside the bundle. It never travels as an HTTP body, but it is part of the contract, so it is documented here — the CLI writes it, cp-public reads it, and the config generator consumes it.

FieldTypeRequiredDescription
mainobjectNoThe path of the worker entrypoint inside the bundle. If it is absent, the deployment is a purely static site (the same model as Workers Static Assets).
assetsobjectNoThe path of the static assets directory inside the bundle
compatibilityDatestringNo
notFoundstringNoThe answer for a path that is not in the assets. With spa, extensionless paths are answered with /index.html (client-side routing). Defaults to 404. Paths that do have an extension still return 404 even under spa — a missed bundle must not become a silent 200.
compatibilityFlagsstring[]NoThe workerd compatibility flags to enable for the worker. Only flags on the allow-list (internal/bundle. AllowedCompatibilityFlags) are accepted; anything else makes the deploy fail — unsafe_module alone hands the process to user code. experimental is added by the generator itself, so do not write it. nodejs_compat: for libraries that require node built-in modules (node:events, node:util, …) such as TypeORM, Sequelize and Knex. The CLI writes this flag when the bundle imports a node built-in.
modulesManifestModule[]Noworkerd modules shipped alongside the entrypoint. Today it is only wasm — workerd blocks WASM compilation inside the bundle, so WASM (such as the Prisma 7 query compiler) can enter only this way. name is exactly the specifier the worker code imports, and path is a file inside the bundle. The CLI writes one entry for each .wasm import the bundle left behind.
frameworkstringNoA marker that the deploy tool bundled the project through a framework adapter (docs/nextjs.md §3). With next, the node additionally renders the two bindings OpenNext looks for (the ASSETS alias and WORKER_SELF_REFERENCE). An unknown value makes the deploy fail.
triggersManifestTriggersNo
emailbooleanNoDeclares email sending and receiving (docs/email.md). When true, the worker gets env.email and export default { email() } becomes the receive handler. It only means something with main — static assets alone have no code to send or receive with.

ManifestTriggers

Scheduled execution. Carried into the bundle exactly as written in runlot.json. It has the same shape as wrangler’s triggers because people migrating copy those three lines verbatim.

Cron is the only trigger we know today, but this is an object so that adding queues or email later does not change what users write.

FieldTypeRequiredDescription
cronsstring[]NoFive-field cron expressions (minute hour day-of-month month day-of-week), in UTC. One-minute granularity, up to 5 per project (bundle.MaxCronsPerProject); the per-organization cap comes from the plan (plan.CronLimitFor). Expressions are really parsed at deploy time — a malformed one, or one that will never fire (0 0 30 2 *), is a 400. Accepting them on shape alone would mean the deploy succeeds and nothing ever happens. triggers requires main: an assets-only deploy uses the generated entry point, which has no scheduled.

ManifestModule

FieldTypeRequiredDescription
namestringYesThe specifier the worker imports. Alphanumerics and ._-, separated by slashes, and it must not start with .. worker is the entry module, so it cannot be used.
typestringYes
pathstringYesThe file path inside the bundle

Role

The roles are nested — whatever a viewer can do a member can do too, and whatever a member can do an admin can do too (cp/internal/public/public.go roleRank).

Type: string

Member

FieldTypeRequiredDescription
userIdstringYes
providerstringYes
subjectstringYes
emailobjectNo
nameobjectNo
roleRoleYes
addedAtstringYes

InviteState

expired 는 시각 비교의 결과이지 열이 아니다 — 초대는 만료되었다고 표시되지 않고, 만료 시각이 지났을 뿐이다.

Type: string

Invite

토큰이 없다. 평문 토큰은 메일에만 실리고, cp-public 은 그 열을 읽지도 못한다 (마이그레이션 0043 의 열 단위 GRANT).

FieldTypeRequiredDescription
inviteIdstringYes
emailstringYes
roleRoleYes
stateInviteStateYes
invitedBystringNo부른 사람의 이름 또는 이메일.
createdAtstringYes
expiresAtstringYes
sentAtstringNo비어 있으면 아직 발송 큐에 있다 (발송은 cp-core 의 회차다).
sendErrorstringNo마지막 발송 실패 이유. 성공해도 남을 수 있다.

InviteView

토큰을 든 사람에게 보이는 초대 (인증 없이 답한다).

FieldTypeRequiredDescription
orgSlugstringYes
orgNamestringYes
roleRoleYes
emailstringYes초대를 보낸 주소다. 수락하는 계정의 주소와 다를 수 있고, 화면은 그 사실을 말해야 한다.
invitedBystringNo
stateInviteStateYes
expiresAtstringYes

SecretName

Becomes env.<NAME> in the worker. DB·ASSETS (the uppercase counterparts of the env.db·env.assets bindings) and the RUNLOT_ prefix are reserved — the first two are already bindings, and the last is the namespace the node writes into the process environment (docs/phase6.md B4).

Type: string

SecretMeta

FieldTypeRequiredDescription
nameSecretNameYes
versionintegerYesIncrements by 1 on every PUT. The node decides whether to swap from the digest of the names and versions
updatedAtstringYes

SecretList

FieldTypeRequiredDescription
secretsSecretMeta[]Yes

LogEvent

One line of the log stream. type tells them apart — line is one line of output, ping is the 15-second heartbeat, and end is why the server ended the stream.

FieldTypeRequiredDescription
typestringYes
tsstringNoThe time the node received the line (line)
epochintegerNo
generationintegerNoThe process generation inside the node. It goes up when the process is replaced
streamstringNorunlot marks a line written by the node rather than by the worker — events such as startup, exit codes and the number of dropped lines.
linestringNoWithout the trailing newline. Anything longer than 64 KiB is cut and truncated is set
truncatedbooleanNo
reasonstringNoThe reason for end: process_exited·client_gone·node_shutdown

Usage

FieldTypeRequiredDescription
projectIdstringYes
hoursintegerYes
rowsUsageRow[]Yes
totalsobjectYes
dbBytesintegerYesThe most recently observed storage bytes. 0 if there is no observation
objBytesintegerYesThe most recently observed object bytes. It is a gauge, not a sum — adding it up per hour counts the same storage once for every hour (docs/storage.md §4.5).

UsageRow

FieldTypeRequiredDescription
hourstringYesStart of the hour (UTC
requestsintegerYes
cpuMsintegerYes
dbBytesintegerYes
objBytesintegerYesGauge (last observed value)
objOpsintegerYesCumulative (sum of deltas)
mailOutintegerYesCumulative (sum of deltas)
mailInintegerYesCumulative (sum of deltas)

EmailStatus

The project's email state (docs/email.md §5).

FieldTypeRequiredDescription
grantedbooleanYesWhether it is on. The existence of the project_email row is the only representation (0038).
addressstringYesThe project's mail domain. Receiving covers all of *@<address>.
fromstringYesThe sender address — the From of mail sent with env.email.send.
perHourintegerYesHourly send limit. Shared with auth mail.
sentLastHourintegerYesMessages sent in the current hour
receivedLastHourintegerYesMessages received in the current hour
sent24hintegerYesMessages sent in the last 24 hours
received24hintegerYesMessages received in the last 24 hours
mailDomainstringYesThis deployment's mail domain suffix. Empty means email cannot be turned on.

MailLogEntry

One row of the mail log (docs/email.md §6.1).

FieldTypeRequiredDescription
mailIdstringYes
projectIdstringYes
directionstringYes
atstringYes
fromstringYes
tostring[]Yes
subjectstringYes
outcomestringYesOutbound: sentdelivered·bounced·complained (relay feedback, docs/email.md §6.2) or failed / Inbound: ok·rejected·no_handler·exception
detailstringNoThe rejection or error sentence — one line for a person to read
authResultsstringNoAuthentication-Results of a received message
sizeBytesintegerYes
hasRawbooleanYesWhether the raw message exists

MailLogPage

FieldTypeRequiredDescription
entriesMailLogEntry[]Yes
cursorstringNoThe next page. Absent means the end
keepDaysintegerYesThe retention period in days for this org

BillingStatus

FieldTypeRequiredDescription
enabledbooleanYes이 배치에 결제가 설정돼 있다
sandboxbooleanYesPolar 테스트 환경 — 실제 청구 없음
planstringYes
statusstringNo프로의 상태. canceled 는 기간 끝까지 프로. free 는 없다
periodEndstringNo현재 결제 기간의 끝
hasPortalbooleanYes결제 이력이 있어 포털을 열 수 있다
projectCountintegerYes지금 프로젝트 수. 무료로 내려간 뒤 한도를 넘긴 상태를 화면이 말한다 — 넘긴 것은 지우지 않고 새 생성만 막힌다
limitsobjectYes

PlanLimits

요금제 하나의 한도 (docs/limits.md §1)

FieldTypeRequiredDescription
projectsintegerYes
storageBytesintegerYes
backupKeepintegerYes보존 세대 수 (= 시간)
cronsintegerYes
mailPerHourintegerYes

StorageStatus

The state of the project's dedicated object storage (docs/storage.md §7).

Objects are not part of DB generationsrunlot pg restore does not roll files back (§4.6). Read-after-write is guaranteed, but list can lag.

FieldTypeRequiredDescription
grantedbooleanYesWhether storage has been enabled. The existence of the enablement record is the only representation (migration 0022), so there is no "configured but off" state.
prefixstringYesThe prefix of the real object keys. User keys are stored only under it, and it is the node-agent that prepends the prefix. If the user key had to carry the project ID, changing it in the request would be enough to reach another project's bucket (§4.4). Empty string when storage is not enabled.
quotaBytesintegerYesThe cap (1 GiB on the free plan). Above it only put and presign(PUT) are rejected, while get·list·delete stay allowed — blocking deletes along with writes would leave a project trapped at its limit with no way out (§4.5). 0 when nothing is granted.
usedBytesintegerYesThe last observed bytes. It lags by the node's reporting interval, so the quota can be exceeded slightly in between (an open item in §10). 0 if there is no observation.

AccessPolicy

The deployment access policy (docs/access.md §3.1). It exposes state only, never secret values. The password and the bypass key are stored as hashes only, and the read API does not return the values (docs/access.md §6, the rule for secret surfaces).

A project that has never set a policy has mode: public and no updatedAt. The two are kept apart because the UI states "this deployment is public" as a status sentence (§7).

FieldTypeRequiredDescription
modestringYespublic is anyone, org is member or above in the org the project belongs to, and password is one shared password per project. There are no other modes because these values are the basis of the authorization (A3) policy — "anyone whose email domain is @acme.com" is not a question about the boundary.
hasPasswordbooleanYesWhether a password is stored. The value is never returned
hasBypassbooleanYesWhether a bypass key for automation exists. The value is shown only once, when it is issued
updatedAtstringNoOmitted if it has never been set

AccessBypass

The bypass key for automation. Included in this response only. It cannot be viewed again, and reissuing invalidates the previous key immediately (docs/access.md §3.6).

FieldTypeRequiredDescription
secretstringYesSend it as-is in the Runlot-Access-Bypass header

AccessAuthorize

The URL that leads back to the protected deployment. A one-time code (60 seconds, single use, scoped to that project) is carried in the query; the front exchanges it, sets the cookie, and then 302s back to the original URL without the query. That 302 is what removes the code from the address bar (docs/access.md §3.4).

FieldTypeRequiredDescription
redirectstringYes

AuthProvider

The social login providers for which a per-project OAuth app can be registered. The providers supported today are GitHub, Google and Kakao; more will be added as demand appears (docs/auth.md §12 — SAML and OIDC when enterprise customers arrive).

Type: string

AuthSettings

End-user auth settings (docs/auth.md §9). The email template editor, MFA and the device list are explicitly out of scope in §12.

FieldTypeRequiredDescription
allowSignupbooleanYesWhether new sign-ups are accepted. When it is off, only people who already exist can log in — used for invite-only apps or for closing a beta.
requireVerifiedEmailbooleanYesWhether email verification is required before login. When it is on, an unverified user gets no session from env.auth.user(request).
sessionDaysintegerYesThe lifetime of the session cookie, in days. It also affects the size of the session table. The project DB lives within the 150 MB cap (docs/auth.md §13), and 10,000 active users on three devices each amount to 6 MB of session data.
brandAuthBrandYes

AuthBrand

Branding for the hosted login page (docs/auth.md §4). If you need finer branding, build your own page and call the JSON endpoints in §5. When every value is an empty string, the project name and the default design tokens are used.

FieldTypeRequiredDescription
namestringYesThe name shown at the top of the page. Empty falls back to the project name
logostringYesThe logo image URL. Empty means no logo is drawn
colorstringYesThe accent color #rrggbb. Empty falls back to the @runlot/design default

AuthProviderMeta

One registered login provider. The secret is not included. The client ID is a public value that is exposed to the browser anyway, while the client secret is encrypted and appears in no response (the rule for secret surfaces, docs/access.md §6).

FieldTypeRequiredDescription
providerAuthProviderYes
clientIdstringYesThe public id issued by the provider
updatedAtstringYesThe time it was last registered or replaced

AuthStatus

The end-user auth state of the project (docs/auth.md §9).

The user table is not part of this API. User data is stored in the runlot_auth schema of the project's Postgres (§6), and that table is handled together with project deletion, pg export, generation backups and restores. If the CP served this table, the data lifecycle would be split in two.

FieldTypeRequiredDescription
grantedbooleanYesWhether auth has been enabled. The existence of the enablement record is the only representation, so there is no "configured but off" state. When it is off, settings holds the defaults and providers is an empty array.
settingsAuthSettingsYes
providersAuthProviderMeta[]YesThe registered login providers. Sorted by name, not by registration order
hostsstring[]YesEvery hostname this project answers on. Each host needs its own callback URL registered in the login provider's console — https://<host>/__runlot/auth/callback/<provider>. Every time you attach a host, add its callback URL to the login provider. That is why the dashboard and the CLI hand you this list verbatim. On a host you missed, login fails only on that host, and the reason is visible only in the provider's console. There are two built-in hostnames — <project>.<org>.<domain> and the joined legacy name <project>--<org>.<domain>. Deploy registers both, so links already handed out keep working, which is why both need a callback URL.

AuthSettingsPatch

A partial update of the settings. Only the fields you send change. Sending the whole settings object for every single switch would let a later save overwrite an earlier one when two users edit the same settings.

FieldTypeRequiredDescription
settingsobjectYesA subset of AuthSettings. An empty object changes nothing

AuthProviderPut

The credentials of a login provider. Replacement goes through the same PUT, so both values are required. If you sent only the ID and omitted the secret, the response could not tell you whether the previous secret survived.

FieldTypeRequiredDescription
clientIdstringYesThe public id issued by the provider
clientSecretstringYesStored encrypted. It never appears in any response again. If you lose it, issue a new one in the login provider's console and PUT it again.

Hostname

One hostname connected to the project. state moves pending_dns (verifying ownership) → pending_tls (issuing the certificate) → active (connected), and the failure state is error. Only active records are in the routing table.

FieldTypeRequiredDescription
hostnamestringYesThe name normalized to lowercase punycode
kindstringYesdefault is the built-in name — both <project>.<org>.<app-domain> and the joined legacy alias <project>--<org>.<app-domain> are this kind. custom is the customer's own domain
statestringYes
lastErrorstringNoWhy the last check failed. Users read it while fixing their own DNS
createdAtstringYes
verifyHostnameVerifyNo

HostnameVerify

The two records the customer has to add to their own DNS. Either one of them is enough to prove ownership — the CNAME also completes the connection, so that is the default guidance. The values are computed and returned by the CP (the fallback origin is a placement setting, so if clients wrote it down themselves they would diverge the day it changes).

FieldTypeRequiredDescription
cnameHostnameRecordYes
txtHostnameRecordYes

HostnameRecord

FieldTypeRequiredDescription
namestringYesRecord name
targetstringNoCNAME target
valuestringNoTXT value

HostnameList

FieldTypeRequiredDescription
hostnamesHostname[]Yes

Zone

One zone in our authoritative DNS (docs/domains.md §4.3). serial is the revision — it goes up on every change, and once publishedSerial catches up, the nameservers are serving that content.

FieldTypeRequiredDescription
zoneIdstringYes
orgIdstringYes
namestringYesexample.com (lowercase punycode, no trailing dot)
serialintegerYes
publishedSerialintegerYesThe serial last written to the zone file. Lower than serial means it is still being applied (usually 2 seconds)
dnssecstringYes
dsstring[]YesThe DS record to publish at the parent. If we are the registrar, D3 publishes it; otherwise the UI shows it
delegationstringYesThe result of the periodic check: whether the NS records at the registrar point at us
delegationCheckedAtstringNo
nameserversstring[]YesThe NS records to enter at the registrar. Not the zone's own values but a copy of the CP configuration
createdAtstringYes

ZoneDetail

One zone and all of its records.

Type: Zone & object

RRset

The set of values for one (name, type) pair. If managedBy is present the row was created by another layer and is locked in the editor — system (the zone's NS), hostname:<host> (app binding), acme (certificate).

FieldTypeRequiredDescription
namestringYesRelative name. @ is the apex
typestringYes
ttlintegerYes
recordsstring[]Yes
managedBystringNo
updatedAtstringYes

ZoneList

FieldTypeRequiredDescription
zonesZone[]Yes

DomainAvailability

Availability and price for one name. There is no wholesale price — cost is a fact of our operations, not a fact for the customer (cp-core does return it for operators).

FieldTypeRequiredDescription
namestringYes
availablebooleanYes
premiumbooleanYesA name the registry has priced separately. Registration and renewal prices differ widely
priceCentsintegerYesFirst registration, 1 year (not KRW — cents)
renewalCentsintegerYesRenewal, 1 year

DomainContact

Registrant contact. It belongs to the customer — the ICANN verification mail goes to this address, and if it is not confirmed within 15 days the domain is suspended. The screen must say so before registration (docs/domains.md §5.5).

FieldTypeRequiredDescription
firstNamestringYes
lastNamestringYes
orgstringNo
emailstringYes
phonestringYes
address1stringYes
address2stringNo
citystringYes
statestringNo
zipstringYes
countrystringYesISO 3166-1 alpha-2

Domain

One domain. Expiry, lock and nameservers are a copy of the registrar's authoritative record, reconciled by domain-sync (6 h) (docs/domains.md §5.4).

FieldTypeRequiredDescription
namestringYes
zoneIdstringNoOur zone. Always created on purchase or import
registrarstringYesexternal means the domain sits in someone else's account — renewal and locking are not ours to do
statestringYesDerived from the expiry date: expiring from 30 days before expiry, expired for the 30-day grace period after it, redemption for the 30 days after that (redemption fee), then released. transferred_out is when the registrar answered "not in our account".
expiresAtstringNo
autoRenewbooleanYesThis is the customer's intent. Auto-renew on the registrar side is always on — we renew first and bill afterwards (§5.4).
lockedbooleanYesTransfer lock
nameserversstring[]Yes
yearsintegerYes
priceCentsintegerNoThe price the customer paid
sagaStepstringYesAnything other than done means the purchase is still in progress (docs/domains.md §5.3)
sagaErrorstringNoWhy it stopped. It can be present even when the state is not failed
createdAtstringYes

DomainEvent

One ledger line (append-only). kind is one of purchase·register_attempt· register·register_reconciled·zone·ds·done·renew·lock· unlock·auto_renew·auth_code·state·transfer_out·import· error and so on. Transfer codes and secrets are never stored here.

FieldTypeRequiredDescription
eventIdintegerYes
kindstringYes
detailobjectNo
atstringYes

DomainDetail

Type: Domain & object

DomainList

FieldTypeRequiredDescription
domainsDomain[]Yes

On this page