86 lines
3.4 KiB
Markdown
86 lines
3.4 KiB
Markdown
# SelfServe
|
|
|
|
Decide whether self-service tenant provisioning is safe to run, and whether
|
|
the job queue running it is healthy.
|
|
|
|
SelfServe does not provision anything. It is the gate around a provisioning
|
|
worker you already have, for example one that claims jobs with
|
|
`FOR UPDATE SKIP LOCKED`, renders a per-tenant stack, starts it and wires
|
|
DNS. Reimplementing that worker would be duplication. The missing half is the
|
|
part that decides whether to let it run, which is the part that keeps
|
|
self-service from turning into an outage.
|
|
|
|
It is pure, with no clock and no I/O. `now` is a parameter.
|
|
|
|
## What it checks and why
|
|
|
|
1. Dormant is not proven. Built, enabled and proven in production are three
|
|
separate states. A worker that is switched off is NOT ASSESSED, never
|
|
ready, and an enabled worker that has never completed a real tenant gets
|
|
its own finding. Until one tenant has gone end to end, the first customer
|
|
is the integration test.
|
|
2. Self-service with no ceiling is a denial of service you run against
|
|
yourself. If every tenant is a stack on one host, a signup loop with no
|
|
quota fills that host, and a full disk on a shared host wedges every
|
|
container, not only the new one. Declaring no limits at all is a critical
|
|
failure: a quota that was never set was never considered.
|
|
`preflight()` also checks the host ceiling, the per-account ceiling and
|
|
the free disk left after provisioning. Unmeasured disk, or a stack with no
|
|
size estimate, is NOT ASSESSED.
|
|
3. A claimed job looks like progress. With `SKIP LOCKED` claiming, a worker
|
|
that dies mid-job leaves the job claimed forever. It is not queued, not
|
|
failed, and nothing retries it, so no dashboard alarms on it.
|
|
`stuckJobs()` finds jobs claimed for longer than a window (15 minutes by
|
|
default).
|
|
4. Provisioned is not reachable. DNS wired and the stack up is not the same
|
|
as the tenant's URL serving. Without a post-provision probe, the first
|
|
person to discover a broken tenant is the tenant.
|
|
|
|
## Usage
|
|
|
|
```ts
|
|
import { assessService, formatService, preflight, stuckJobs, type Job } from '@extant2000/self-serve'
|
|
|
|
const jobs: Job[] = [
|
|
{ id: 'j7', state: 'claimed', claimedAt: '2026-09-28T11:00:00Z', claimedBy: 'worker-1', where: 'jobs table' },
|
|
{ id: 'j8', state: 'queued', where: 'jobs table' },
|
|
]
|
|
|
|
const report = assessService(
|
|
{ workerEnabled: true, provenInProduction: false, verifiesReachability: true },
|
|
{
|
|
now: '2026-09-28T12:00:00Z',
|
|
request: { account: 'acme', tenantName: 'acme-prod', existingForAccount: 1, estimatedDiskBytes: 2e9, where: 'jobs table, j8' },
|
|
host: { tenants: 10, freeDiskBytes: null, where: 'df -P /data' },
|
|
limits: { maxTenants: 50, maxPerAccount: 3, minFreeDiskBytes: 10e9 },
|
|
jobs,
|
|
},
|
|
)
|
|
|
|
for (const f of report.findings) console.log(`${f.determination.padEnd(13)} ${f.id}: ${f.summary}`)
|
|
```
|
|
|
|
`assessService()` returns a `CapabilityReport` from
|
|
`@extant2000/evidence-record`, and `formatService()` renders it for a
|
|
terminal or CI log. `preflight()` and `stuckJobs()` can be used on their own.
|
|
|
|
## Sample output
|
|
|
|
Produced by running the example above:
|
|
|
|
```
|
|
not-assessed worker/unproven: The worker is enabled but has never completed a real tenant
|
|
not-assessed capacity/disk: Disk headroom after provisioning cannot be computed
|
|
fail job/j7: job j7 has been claimed by worker-1 since 2026-09-28T11:00:00Z
|
|
```
|
|
|
|
## Install
|
|
|
|
```
|
|
npm install @extant2000/self-serve
|
|
```
|
|
|
|
## License
|
|
|
|
MIT. Copyright (c) 2026 Extant 2000 LLC. See [LICENSE](LICENSE).
|