@zerotal/arch 1.13.2 → 1.13.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/docs/changelog.md +81 -0
- package/docs/deployment.md +13 -0
- package/docs/index.md +1 -1
- package/docs/queue.md +18 -0
- package/docs/scheduler.md +63 -0
- package/docs/site-gate.md +179 -0
- package/package.json +3 -3
package/docs/changelog.md
CHANGED
|
@@ -27,6 +27,87 @@ the section for every version you cross and apply its migration notes, not only
|
|
|
27
27
|
majors. [Releases and versioning](/docs/support-policy#releases-and-versioning) explains
|
|
28
28
|
when that carve-out ends.
|
|
29
29
|
|
|
30
|
+
## 1.13.3 — 2026-08-31
|
|
31
|
+
|
|
32
|
+
Two things an app cannot see about itself, from two field reports. Both are the same
|
|
33
|
+
shape as most of this month's work: state that is real, consequential, and invisible from
|
|
34
|
+
inside the process that would want to know it.
|
|
35
|
+
|
|
36
|
+
### Added
|
|
37
|
+
|
|
38
|
+
- **A site gate — maintenance, and private preview.**
|
|
39
|
+
[Guide](/docs/site-gate) · `zt down` · `zt preview` · `zt up` · `zt gate:status`
|
|
40
|
+
|
|
41
|
+
Proposed by a team running a hand-edited `basic_auth` block in their reverse proxy,
|
|
42
|
+
deliberately kept out of version control so it could not be deployed and forgotten into
|
|
43
|
+
a live shop. That precaution is the feature request: the gate belongs where the app can
|
|
44
|
+
reason about it.
|
|
45
|
+
|
|
46
|
+
Two states that look alike and are not. **Maintenance** means the site is down —
|
|
47
|
+
everyone refused, staff included, because the usual reason a site is down is that its
|
|
48
|
+
database is being changed underneath it. **Private preview** means the site is up and
|
|
49
|
+
working, for the people invited to it, for weeks.
|
|
50
|
+
|
|
51
|
+
The details that make it framework work rather than app work:
|
|
52
|
+
|
|
53
|
+
- **Maintenance is always `503` with `Retry-After`, and is not configurable.** A
|
|
54
|
+
maintenance page served at `200` tells a search engine the apology is your homepage,
|
|
55
|
+
and it will index it as such.
|
|
56
|
+
- **A preview token is stripped from the URL on first use**, by redirecting to a
|
|
57
|
+
cookie. Left in the address bar it travels into `Referer` on every outbound link,
|
|
58
|
+
into analytics, and into screenshots.
|
|
59
|
+
- **Webhook paths must be declared** in `gate.allow`. A payment provider posting a
|
|
60
|
+
settlement into a maintenance window otherwise gets a 503 — a retry, a dropped
|
|
61
|
+
callback, or a payment your books never learn about.
|
|
62
|
+
- **The state is a file, and the token is stored hashed.** A flag in the database is
|
|
63
|
+
unreadable exactly when the database is what you are working on; a token in a file
|
|
64
|
+
is a credential in something every backup copies.
|
|
65
|
+
- **It covers `Router.raw()` routes.** Found by running it: this framework's own docs
|
|
66
|
+
site serves every `/docs/*` page from a raw route, so an early build gated the front
|
|
67
|
+
page — which is what a person checks — and left all the content public.
|
|
68
|
+
- **The state file is gitignored** by the scaffold, which is the entire point.
|
|
69
|
+
|
|
70
|
+
- **Worker liveness — `zt doctor` can tell whether anything is running your background
|
|
71
|
+
work.** [Schedules](/docs/scheduler#is-anything-actually-running-them) ·
|
|
72
|
+
[Queue](/docs/queue#is-a-worker-running)
|
|
73
|
+
|
|
74
|
+
An app could say what it _registered_ and nothing could say whether any of it ever
|
|
75
|
+
_ran_. The reported failure: a team shipped to production with no worker process, and
|
|
76
|
+
every scheduled task silently did not execute for weeks. No hold was released, no
|
|
77
|
+
reminder was sent, nothing logged — from the web process's point of view nothing was
|
|
78
|
+
wrong, and they found it by going looking.
|
|
79
|
+
|
|
80
|
+
```
|
|
81
|
+
✖ Scheduler — 3 schedule(s) registered, and no worker has ever checked in.
|
|
82
|
+
Nothing is running them.
|
|
83
|
+
fix: Start the worker process: `bun zt worker`.
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
The beat lives in the **cache**, because the process reading `doctor` is not the process
|
|
87
|
+
running the work and often not the same machine — and your cache driver already decides
|
|
88
|
+
what shared state can see. On `memory`, which is private to each process, the check
|
|
89
|
+
**says it cannot tell** rather than reporting a missing worker: a check that cried wolf
|
|
90
|
+
on every app using that driver is one people would learn to skip, and then it would not
|
|
91
|
+
be there for the case it exists for.
|
|
92
|
+
|
|
93
|
+
`@zerotal/core/heartbeat` exposes the primitive if you want the same signal on an ops
|
|
94
|
+
page.
|
|
95
|
+
|
|
96
|
+
### Fixed
|
|
97
|
+
|
|
98
|
+
- **`secureHeaders: false` no longer empties the kernel middleware.** It set the layer to
|
|
99
|
+
`[]`, which was the same thing as removing the headers right up until the site gate
|
|
100
|
+
joined it — at which point opting out of security headers would silently have taken the
|
|
101
|
+
gate with it. One feature's opt-out disabling another's is precisely what the gate is
|
|
102
|
+
otherwise about.
|
|
103
|
+
|
|
104
|
+
### Documented
|
|
105
|
+
|
|
106
|
+
- **Minting `APP_KEY` without the code.** `key:generate` is part of the application, so it
|
|
107
|
+
exists only once a release is installed — awkward when preparing `.env` first, since
|
|
108
|
+
`migrate` wants the file and the file wants a key. `openssl rand -base64 32` produces
|
|
109
|
+
exactly what `key:generate` writes; the [deployment guide](/docs/deployment) now says so.
|
|
110
|
+
|
|
30
111
|
## 1.13.2 — 2026-08-31
|
|
31
112
|
|
|
32
113
|
From a production field report at 1.12.0 — an Inertia + React app on SQLite, 117 routes,
|
package/docs/deployment.md
CHANGED
|
@@ -213,6 +213,19 @@ DATABASE_URL=postgres://user:pass@db-host:5432/app
|
|
|
213
213
|
and a `base64:`-prefixed one are accepted. Generate it **on the server** — a key carried
|
|
214
214
|
from a laptop is a key that has been in a shell history and a scrollback buffer.
|
|
215
215
|
|
|
216
|
+
**On a first deploy you do not have to wait for the code.** `key:generate` is part of the
|
|
217
|
+
application, so it only exists once the release is installed — which is awkward if you are
|
|
218
|
+
preparing `.env` before that, since `migrate` wants the file and the file wants a key.
|
|
219
|
+
There is nothing framework-specific about the value:
|
|
220
|
+
|
|
221
|
+
```bash fragment
|
|
222
|
+
openssl rand -base64 32
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
That is exactly what `key:generate` produces — 32 random bytes, base64 — so a key minted
|
|
226
|
+
this way is indistinguishable from one it wrote. Use whichever fits the order you deploy
|
|
227
|
+
in.
|
|
228
|
+
|
|
216
229
|
`APP_ENV` accepts deployment names like `production` and `staging`; they all normalize to
|
|
217
230
|
the `web` runtime mode — they describe _where_ the app runs, not _how_. See
|
|
218
231
|
[Configuration](/docs/config-system).
|
package/docs/index.md
CHANGED
|
@@ -40,7 +40,7 @@ cd my-app && bun dev
|
|
|
40
40
|
| See what's running in production | [Logger](/docs/logger) · [Monitor](/docs/monitor) · [Telemetry](/docs/telemetry) · [Health](/docs/health) |
|
|
41
41
|
| Debug what it just did | [DevTools](/docs/devtools) · [Errors](/docs/errors) |
|
|
42
42
|
| Work with a coding agent | [Agent Surface](/docs/arch) |
|
|
43
|
-
| Ship it | [Deployment](/docs/deployment) · [Commands](/docs/commands)
|
|
43
|
+
| Ship it | [Deployment](/docs/deployment) · [Site gate](/docs/site-gate) · [Commands](/docs/commands) |
|
|
44
44
|
|
|
45
45
|
## Choosing a frontend
|
|
46
46
|
|
package/docs/queue.md
CHANGED
|
@@ -353,6 +353,24 @@ needed, so chaining works with any driver.
|
|
|
353
353
|
|
|
354
354
|
## Processing jobs
|
|
355
355
|
|
|
356
|
+
### Is a worker running?
|
|
357
|
+
|
|
358
|
+
Jobs waiting with nothing to run them is the same failure as an unrun schedule, one layer
|
|
359
|
+
over: the queue fills, nothing errors, and you find out when a customer asks where their
|
|
360
|
+
email went.
|
|
361
|
+
|
|
362
|
+
`zt doctor` reports it, keyed on the **pending depth** rather than on whether any job
|
|
363
|
+
class exists — an app with an empty queue and no worker may be perfectly fine:
|
|
364
|
+
|
|
365
|
+
```
|
|
366
|
+
✖ Queue worker — 4102 job(s) waiting, and no worker has ever checked in.
|
|
367
|
+
fix: Start the worker process: `bun zt worker`.
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
The same shared-cache caveat applies as for [schedules](/docs/scheduler#is-anything-actually-running-them):
|
|
371
|
+
the beat lives in the cache, so a `memory` driver means the check stands aside rather
|
|
372
|
+
than guessing.
|
|
373
|
+
|
|
356
374
|
### Dedicated worker process
|
|
357
375
|
|
|
358
376
|
The standard way to process jobs in production is a long-running worker process:
|
package/docs/scheduler.md
CHANGED
|
@@ -301,6 +301,69 @@ Scheduled tasks (2)
|
|
|
301
301
|
Next run 2026-06-22T06:00:00.000Z
|
|
302
302
|
```
|
|
303
303
|
|
|
304
|
+
## Is anything actually running them?
|
|
305
|
+
|
|
306
|
+
Schedules register in the `worker` and `console` environments, not in `web`. That is the
|
|
307
|
+
right design — HTTP instances should not run cron — and it means **a second process is
|
|
308
|
+
required.** The framework starts happily without one.
|
|
309
|
+
|
|
310
|
+
An app shipped to production with no worker, and every scheduled task silently did not
|
|
311
|
+
execute for weeks. No hold was released, no reminder was sent, nothing logged, because
|
|
312
|
+
from the web process's point of view nothing was wrong. They found it by going looking.
|
|
313
|
+
|
|
314
|
+
`zt doctor` now looks for you:
|
|
315
|
+
|
|
316
|
+
```
|
|
317
|
+
✖ Scheduler — 3 schedule(s) registered, and no worker has ever checked in. Nothing is
|
|
318
|
+
running them. This is silent by nature — the web process has no way to notice, and
|
|
319
|
+
the work simply does not happen.
|
|
320
|
+
fix: Start the worker process: `bun zt worker`. It is a second process; the web
|
|
321
|
+
server does not run this.
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
The worker records a check-in every minute while it runs. A check-in older than fifteen
|
|
325
|
+
minutes is a warning rather than a failure, because a worker mid-restart is not a
|
|
326
|
+
missing worker.
|
|
327
|
+
|
|
328
|
+
### It needs a shared cache to mean anything
|
|
329
|
+
|
|
330
|
+
The beat is written to the cache, because the process reading `doctor` is not the
|
|
331
|
+
process running the schedules — and often not the same machine. Your cache driver
|
|
332
|
+
decides what that can see:
|
|
333
|
+
|
|
334
|
+
| Driver | Sees a worker on… |
|
|
335
|
+
| ------------------ | --------------------------------------- |
|
|
336
|
+
| `sqlite` (default) | another process on the same box |
|
|
337
|
+
| `redis` | another machine |
|
|
338
|
+
| `memory` | nothing — it is private to each process |
|
|
339
|
+
|
|
340
|
+
On `memory` the check **stands aside and says so** rather than reporting a missing
|
|
341
|
+
worker. A check that cried wolf on every app using the memory driver is one people would
|
|
342
|
+
learn to skip, and then it would not be there for the case it exists for.
|
|
343
|
+
|
|
344
|
+
### Reading it yourself
|
|
345
|
+
|
|
346
|
+
The primitive is `@zerotal/core/heartbeat`, if you want the same signal on an ops page:
|
|
347
|
+
|
|
348
|
+
```ts
|
|
349
|
+
import { Heartbeat } from "@zerotal/core/heartbeat";
|
|
350
|
+
|
|
351
|
+
const seen = await Heartbeat.lastSeen("scheduler");
|
|
352
|
+
// { status: "seen", ageSeconds, beat } | { status: "never" } | { status: "unknown", reason }
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
| Name | Description |
|
|
356
|
+
| --------------------- | ---------------------------------------------------------------------------------------------------------- |
|
|
357
|
+
| `Heartbeat` | `beat()` records a check-in, `start()` beats on an interval and returns a stopper, `lastSeen()` reads one. |
|
|
358
|
+
| `Beat` | What a check-in records — `at`, `pid`, `detail`. |
|
|
359
|
+
| `BeatLookup` | The three answers: `seen`, `never`, and `unknown` with a reason. |
|
|
360
|
+
| `workerLivenessCheck` | Builds the doctor check above for a worker kind. |
|
|
361
|
+
| `describeBeat` | Renders a `BeatLookup` as the prose used in the report. |
|
|
362
|
+
|
|
363
|
+
`unknown` is a distinct answer from `never` on purpose, and every consumer has to keep
|
|
364
|
+
them apart: one means nothing is running, the other means this process cannot see whether
|
|
365
|
+
anything is.
|
|
366
|
+
|
|
304
367
|
## Run history
|
|
305
368
|
|
|
306
369
|
Every completed execution — success or failure — is recorded to a capped JSONL
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Site gate
|
|
3
|
+
description: Maintenance mode and private preview — take a site down correctly, or open it only to the people you invite.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Site gate
|
|
7
|
+
|
|
8
|
+
Two states a site can be in that are not "serving the public", and they are not the
|
|
9
|
+
same thing.
|
|
10
|
+
|
|
11
|
+
**Maintenance** — the site is _down_. Nobody may use it, staff included, because the
|
|
12
|
+
usual reason a site is down is that its database is being changed underneath it, and
|
|
13
|
+
letting one person in is letting them into that. Minutes, not weeks. Every request is
|
|
14
|
+
answered `503` with `Retry-After`.
|
|
15
|
+
|
|
16
|
+
**Private preview** — the site is _up and working perfectly_, for the people invited to
|
|
17
|
+
it. Not down, not broken, not public yet. Weeks. An invited visitor gets the real site
|
|
18
|
+
at `200` and can transact on it; the public gets a holding page.
|
|
19
|
+
|
|
20
|
+
Building one and calling it both is the usual mistake, and it is why a pre-launch site
|
|
21
|
+
ends up behind a `503` that search engines take seriously.
|
|
22
|
+
|
|
23
|
+
## Turning it on
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
bun zt down --retry=120 # maintenance
|
|
27
|
+
bun zt preview # private preview; prints the link
|
|
28
|
+
bun zt preview --until=2026-09-30
|
|
29
|
+
bun zt up # open again
|
|
30
|
+
bun zt gate:status # what is it doing
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
`zt preview` generates the token when you do not pass one, because a token typed by a
|
|
34
|
+
person is the only thing between the public and an unlaunched site and nothing
|
|
35
|
+
rate-limits guesses at it. **Keep the link it prints** — only a hash is stored, so the
|
|
36
|
+
token cannot be read back.
|
|
37
|
+
|
|
38
|
+
These four commands need no application and are registered in every environment. A
|
|
39
|
+
maintenance command that only works when the app boots is one you cannot reach at the
|
|
40
|
+
moment you need it.
|
|
41
|
+
|
|
42
|
+
## From your own console
|
|
43
|
+
|
|
44
|
+
The framework's job is the primitive; the button is yours.
|
|
45
|
+
|
|
46
|
+
```ts
|
|
47
|
+
import { Gate } from "@zerotal/core/gate";
|
|
48
|
+
|
|
49
|
+
await Gate.preview({ token, until: "2026-09-30", by: user.name });
|
|
50
|
+
await Gate.maintenance({ retryAfter: 120, by: user.name });
|
|
51
|
+
await Gate.open();
|
|
52
|
+
|
|
53
|
+
Gate.status(); // { mode, since, until, by, expired } — never the token
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Getting in
|
|
57
|
+
|
|
58
|
+
**A secret link.** `https://example.com/?preview=<token>`. The gate recognises the
|
|
59
|
+
token, sets a signed `HttpOnly` `SameSite=Lax` cookie, and **redirects to the same URL
|
|
60
|
+
with the parameter removed.**
|
|
61
|
+
|
|
62
|
+
That redirect is not tidiness. A token left in the address bar travels into `Referer`
|
|
63
|
+
on every outbound link, into analytics, into screenshots, and into the message where
|
|
64
|
+
somebody shares "the page I was looking at". Stripping it on first use leaves the
|
|
65
|
+
secret in a cookie and nowhere else.
|
|
66
|
+
|
|
67
|
+
**A signed-in staff account.** A request whose authenticated user has any role other
|
|
68
|
+
than `customer` is admitted without a token, so an app that already has staff accounts
|
|
69
|
+
does not need a second secret for the same people.
|
|
70
|
+
|
|
71
|
+
The cookie lasts seven days. A preview cookie with no lifetime means an ex-tester keeps
|
|
72
|
+
access to a site that has since gone live with real customer data.
|
|
73
|
+
|
|
74
|
+
**Rotating revokes.** `zt preview --token=<new>` invalidates every cookie issued under
|
|
75
|
+
the previous token, because the way a preview leaks is a tester forwarding the link to
|
|
76
|
+
somebody who has left.
|
|
77
|
+
|
|
78
|
+
## What stays reachable
|
|
79
|
+
|
|
80
|
+
These are open whatever the gate is doing:
|
|
81
|
+
|
|
82
|
+
| Path | Why |
|
|
83
|
+
| ---------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
84
|
+
| `/__zerotal/*` | The health endpoint. Otherwise the uptime monitor pages the on-call about a planned window, and a deploy gate that polls health fails its own release. |
|
|
85
|
+
| `/css/*`, `/js/*`, `/assets/*`, `/favicon.ico` | A maintenance page that 503s its own stylesheet is an unstyled apology. |
|
|
86
|
+
|
|
87
|
+
**Your webhooks are not on that list, and this is the one that costs money.**
|
|
88
|
+
|
|
89
|
+
A payment provider posting a settlement into a maintenance window gets a `503`, and
|
|
90
|
+
depending on the provider that is a retry, a dropped callback, or a payment your books
|
|
91
|
+
never learn about. Nothing can infer which of your routes a third party calls, so it
|
|
92
|
+
has to be declared:
|
|
93
|
+
|
|
94
|
+
```ts
|
|
95
|
+
// config/gate.ts
|
|
96
|
+
export default {
|
|
97
|
+
allow: ["/webhooks/", "/api/callbacks/"],
|
|
98
|
+
};
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## What the public gets during a preview
|
|
102
|
+
|
|
103
|
+
`200` with a holding page by default, which is right for a pre-launch site collecting
|
|
104
|
+
an email address. Set `publicResponse: "notFound"` when the site's existence is itself
|
|
105
|
+
not public:
|
|
106
|
+
|
|
107
|
+
```ts
|
|
108
|
+
// config/gate.ts
|
|
109
|
+
export default { publicResponse: "notFound" };
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Both are legitimate and the framework does not pick. **Maintenance is always `503`** and
|
|
113
|
+
is not configurable, because that one is not a preference: a maintenance page served at
|
|
114
|
+
`200` tells a search engine that "we will be back shortly" is the content of your
|
|
115
|
+
homepage, and it will index it as such. Sites have lost their rankings to a two-hour
|
|
116
|
+
window served at the wrong status code.
|
|
117
|
+
|
|
118
|
+
## Where the state lives
|
|
119
|
+
|
|
120
|
+
`storage/framework/gate.json`, not the database.
|
|
121
|
+
|
|
122
|
+
A flag in the database is unreadable exactly when the database is the thing you are
|
|
123
|
+
working on, so a maintenance mode kept there works only on the days you did not need
|
|
124
|
+
it. The file is also why the gate survives a restart — an in-memory flag would be
|
|
125
|
+
lifted by the very deploy it was supposed to run behind.
|
|
126
|
+
|
|
127
|
+
The preview token is stored as a **hash**. The file sits on disk, readable by anything
|
|
128
|
+
on the box and copied by every backup, and putting a live credential in it would be the
|
|
129
|
+
same mistake as an `.env.example` carrying a working key.
|
|
130
|
+
|
|
131
|
+
A file the gate cannot parse reads as **open**, deliberately. Failing closed would take
|
|
132
|
+
a site down because a JSON file lost a brace, and "the site is up" is the safer error
|
|
133
|
+
for a mechanism whose whole purpose is to be turned off again.
|
|
134
|
+
|
|
135
|
+
## It covers raw routes too
|
|
136
|
+
|
|
137
|
+
`Router.raw()` bypasses the middleware pipeline by design — that is what it is for. It
|
|
138
|
+
does **not** bypass the gate.
|
|
139
|
+
|
|
140
|
+
A gate that covered only the pipeline would be the worst kind, because it gates the
|
|
141
|
+
homepage and therefore looks like it works. This framework's own documentation site
|
|
142
|
+
serves every `/docs/*` page from a raw route: with an early build of the gate on, the
|
|
143
|
+
front page said "coming soon" and every page of content stayed public.
|
|
144
|
+
|
|
145
|
+
Raw routes get no staff bypass, since no session has been resolved by then — the token
|
|
146
|
+
cookie is the only way through.
|
|
147
|
+
|
|
148
|
+
## `zt doctor` reports it
|
|
149
|
+
|
|
150
|
+
A gate is the most reversible thing in the framework and the easiest to forget, because
|
|
151
|
+
when it is working nothing complains: the people who would notice are the ones being
|
|
152
|
+
kept out, and they have no way to tell you.
|
|
153
|
+
|
|
154
|
+
- **Maintenance in production** is reported as a failure. It is an outage.
|
|
155
|
+
- **A preview in production** is a warning naming who set it and when, since a
|
|
156
|
+
pre-launch gate is usually deliberate.
|
|
157
|
+
- **A preview whose `until` has passed** is its own warning: the file says the site is
|
|
158
|
+
gated and it is not, so what a reader believes and what visitors get have come apart.
|
|
159
|
+
|
|
160
|
+
## Reference
|
|
161
|
+
|
|
162
|
+
| Name | Description |
|
|
163
|
+
| -------------------- | ---------------------------------------------------------------------------- |
|
|
164
|
+
| `Gate` | Read and write the gate: `maintenance()`, `preview()`, `open()`, `status()`. |
|
|
165
|
+
| `GateStatus` | What `Gate.status()` returns. Never includes the token. |
|
|
166
|
+
| `MaintenanceOptions` | `retryAfter`, `by`. |
|
|
167
|
+
| `PreviewOptions` | `token`, `until`, `by`. |
|
|
168
|
+
| `GateMiddleware` | The kernel middleware that answers for a closed site. |
|
|
169
|
+
| `GateState` | The shape written to `storage/framework/gate.json`. |
|
|
170
|
+
| `GateMode` | `"maintenance" | "preview"`. |
|
|
171
|
+
| `readGate` | Read the state file, or `null` when the site is open. |
|
|
172
|
+
| `gateExpired` | Whether a preview's `until` has passed. |
|
|
173
|
+
| `GATE_FILE` | Path of the state file, relative to the project root. |
|
|
174
|
+
| `GATE_COOKIE` | Name of the cookie the gate issues. |
|
|
175
|
+
| `GATE_QUERY` | The query parameter that carries a token — `preview`. |
|
|
176
|
+
| `bun zt down` | `DownCommand` — maintenance on. |
|
|
177
|
+
| `bun zt preview` | `PreviewCommand` — private preview on, prints the link. |
|
|
178
|
+
| `bun zt up` | `UpCommand` — open the site. |
|
|
179
|
+
| `bun zt gate:status` | `GateStatusCommand` — what the gate is doing. |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zerotal/arch",
|
|
3
|
-
"version": "1.13.
|
|
3
|
+
"version": "1.13.3",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"maturity": "stable",
|
|
6
6
|
"private": false,
|
|
@@ -35,11 +35,11 @@
|
|
|
35
35
|
"typecheck": "tsc --noEmit"
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@zerotal/core": "1.13.
|
|
38
|
+
"@zerotal/core": "1.13.3"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
41
|
"typescript": "^5.8.0",
|
|
42
|
-
"@zerotal/orm": "1.13.
|
|
42
|
+
"@zerotal/orm": "1.13.3"
|
|
43
43
|
},
|
|
44
44
|
"description": "The Zerotal agent surface — an MCP server that hands coding agents the framework's machine-readable truth: exact API signatures, live routes and schema, version-matched docs, and `zt doctor`.",
|
|
45
45
|
"keywords": [
|