Operating an instance
For whoever runs the box: invitations, ceilings, moderation, and what it costs.
For whoever runs the box. The admin plane answers on its own host and only to an account with the admin role; everywhere else those paths return 404.
Accounts and invitations
- There is no mail service attached and no email address stored, so an invitation is delivered by a person: pasted into a message, read down a phone line, written on paper.
- The dashboard composes that message for you and lets you edit it before sending. Its wording is deliberate — an account invitation and a credential phish have the same shape, so the draft avoids urgency, secrecy and the security-noun cluster that filters score.
- An invitation works once and expires. Revoking an unused one is immediate.
- Sign-up is invitation-only or closed. There is no third mode.
The deployment scope
Ceilings and switches, changed from the admin plane rather than by editing a file and restarting. Lowering a ceiling below current usage warns rather than refusing — it stops new growth without deleting anybody's work.
| Ceiling | Default | Set by |
|---|---|---|
| Links per account | 500 | dashboard, and per account |
| Groups per account | 25 | dashboard, and per account |
| Links shown on one collection | 500 | environment |
| Live API keys per account | 20 | environment |
| Keys an account may ever mint | 100 | environment |
| Passkeys per account | 10 | environment |
| Accounts on the instance | 100 | dashboard |
| Links on the instance | 50,000 | dashboard |
| Masked links on the instance | 5,000 | environment |
| Target URL length | 2,048 characters | environment |
| Code length | 5 random, up to 64 chosen | environment |
| Session lifetime | 14 days | environment |
| Invitation lifetime | 14 days | per invitation |
| Wait between username changes | 90 days | environment |
| Shortest password | 12 characters | environment |
Moderation, and codes
- Disabling an ordinary link is immediate, and a moderated link never follows a fallback — a fallback would be the owner routing around the decision.
- A masked link can be turned off by its code, and that is all: you cannot see where it goes or who made it, because this service holds neither. Both of its addresses then answer the same
404as a code never issued. A strict link's page is cacheable, so a copy already at the edge can keep resolving for up to 30 seconds after it is turned off. See Masked links. - Retired codes are listed, and can be released one at a time. Releasing a code that was printed on something is the one irreversible-feeling action here, so it asks.
- Account holders can ask for a retired code, and those requests queue up with the answers beside them.
What the box is doing
- The metrics panel is about the machine, not about visitors: rows, size, request counts, what this instance costs to run.
- The audit log records administrative action. Purging it keeps the record of the purge.
- Every audited write on this service has a number on it. That is the rule the rate limits come from, and it was learnt the hard way four times.
Rate limits as they are configured now
| What | Limit | Counted per |
|---|---|---|
| Link creation | 20 / 60s | per account |
| Group creation | 10 / 60s | per account |
| Edits to links and groups | 120 / 300s | per account |
| Credential writes — keys, passkeys, two-factor | 10 / 300s | per account |
| QR and print renders | 120 / 60s | per account |
| Sign-in, invitations, two-factor answers | 10 / 300s | per username or token |
| The same, instance-wide | 120 / 60s | one bucket |
| Username availability checks | 60 / 60s | per account |
| Published collection renders | 240 / 60s | per slug |
| The same, instance-wide | 3000 / 60s | one bucket |
| Notes to the operator | 3 / 3600s | per account |
| Masked-link writes — create, retarget, delete, index saves | 20 / 60s | per account |
| The same, instance-wide — index saves are not counted here | 600 / 60s | one bucket |
| Request body | 64 KB | per request, declared or not |
| Following a short link | never limited | — |
Running your own
- One Python process, one SQLite file. No ORM, no template engine, no build step, no CDN, and no outbound HTTP requests at all.
- The service binds to loopback; a tunnel is the only ingress. That is what lets the app tell somebody on the box from somebody on the internet.
- The whole interface is generated in Python and delivered inline in one response, which is what makes the content-security policy as tight as it is: nothing may be fetched, embedded or executed from anywhere.
- Dependencies are pinned by version and hash, and the list is short enough to read in a minute.
The same page as text, for a program: GET https://geodzk.com/api/docs/operating — needs a key holding docs:read.