@voltro/cli 0.22.1 → 0.24.0
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/CHANGELOG.md +427 -0
- package/bin/voltro.mjs +39 -4
- package/dist/{apiBuild-DiWxVz-M.js → apiBuild-B2m4XK_8.js} +45 -23
- package/dist/apiBuild-UEM3QBke.js +2 -0
- package/dist/bin.js +3 -3
- package/dist/{commands-BxRaIOBG.js → commands-CaIhTsC2.js} +2944 -2557
- package/dist/{dbCommand-DVASmZj2.js → dbCommand-CC61CsAc.js} +474 -263
- package/dist/dbCommand-Cr__4ATv.js +2 -0
- package/dist/{dev-Dd3EZzj5.js → dev-DPkQVUTP.js} +1 -1
- package/dist/{dev-BRiPgbKw.js → dev-NCuEhNxs.js} +1593 -1575
- package/dist/index.js +1 -1
- package/dist/{inspectMetrics-9ZSuDeqD.js → inspectMetrics-dcC7gnxu.js} +566 -552
- package/dist/{serveCommand-DXJOARkO.js → serveCommand-C4gzDZzc.js} +425 -411
- package/dist/serveEntry.js +2 -2
- package/dist/{start-CsIjcOi-.js → start-C_4VUDT8.js} +327 -325
- package/dist/startEntry.js +2 -2
- package/package.json +17 -17
- package/templates/AGENTS.md +1 -1
- package/templates/agent-docs/_index.md +1 -1
- package/templates/agent-docs/_manifest.json +1 -1
- package/templates/agent-docs/authentication.md +21 -0
- package/templates/agent-docs/caching.md +21 -5
- package/templates/agent-docs/cli.md +61 -0
- package/templates/agent-docs/data.md +208 -170
- package/templates/agent-docs/database/migrations.md +123 -0
- package/templates/agent-docs/database/seedsdialects.md +12 -0
- package/templates/agent-docs/deployment.md +66 -1
- package/templates/agent-docs/plugins.md +42 -0
- package/templates/agent-docs/routing.md +23 -1
- package/templates/agent-docs/templates/apibackends.md +2 -2
- package/templates/agent-docs/whats-new.md +281 -32
- package/templates/agent-docs/workflows.md +30 -0
- package/templates/apps/api-ai/package.json +8 -7
- package/templates/apps/api-auth/package.json +9 -8
- package/templates/apps/api-backend/package.json +8 -7
- package/templates/apps/api-backend-deactivation/package.json +8 -7
- package/templates/apps/api-backend-mail/package.json +9 -8
- package/templates/apps/api-backend-mariadb/package.json +10 -9
- package/templates/apps/api-backend-storage/package.json +9 -8
- package/templates/apps/api-data-advanced/package.json +9 -8
- package/templates/apps/api-durable/package.json +9 -8
- package/templates/apps/api-feature-flags/package.json +10 -9
- package/templates/apps/api-governance/package.json +9 -8
- package/templates/apps/api-kv/package.json +9 -8
- package/templates/apps/api-moderation/package.json +9 -8
- package/templates/apps/api-observability/package.json +9 -8
- package/templates/apps/api-ratelimit/package.json +9 -8
- package/templates/apps/api-rbac/package.json +9 -8
- package/templates/apps/api-rest/package.json +8 -7
- package/templates/apps/api-saas/package.json +12 -11
- package/templates/apps/api-search/package.json +9 -8
- package/templates/apps/api-versioning/app.config.ts +6 -4
- package/templates/apps/api-versioning/package.json +9 -8
- package/templates/apps/api-webhooks/package.json +10 -9
- package/templates/apps/changelog/package.json +7 -6
- package/templates/apps/edge-functions/package.json +3 -2
- package/templates/apps/frontend-admin/package.json +9 -8
- package/templates/apps/frontend-app/package.json +9 -8
- package/templates/apps/frontend-blank/package.json +8 -7
- package/templates/apps/frontend-contact/package.json +8 -7
- package/templates/apps/frontend-contact/src/globals.d.ts +6 -0
- package/templates/apps/frontend-dashboard/package.json +8 -7
- package/templates/apps/frontend-docs/package.json +8 -7
- package/templates/apps/frontend-docs/src/globals.d.ts +6 -0
- package/templates/apps/frontend-i18n/package.json +7 -6
- package/templates/apps/frontend-landing/package.json +8 -7
- package/templates/apps/frontend-landing/src/globals.d.ts +6 -0
- package/templates/apps/frontend-spa/package.json +8 -7
- package/templates/apps/frontend-spa/src/globals.d.ts +6 -0
- package/templates/apps/frontend-ssr/package.json +8 -7
- package/templates/apps/frontend-ssr/src/globals.d.ts +6 -0
- package/templates/apps/frontend-ssr-api/package.json +9 -8
- package/templates/apps/frontend-static-blog/package.json +7 -6
- package/templates/apps/frontend-static-blog/src/globals.d.ts +6 -0
- package/dist/apiBuild-COyPDf3R.js +0 -2
- package/dist/dbCommand-CO3eSAZR.js +0 -2
package/dist/startEntry.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { t } from "./start-
|
|
1
|
+
import { Z as e } from "./inspectMetrics-dcC7gnxu.js";
|
|
2
|
+
import { t } from "./start-C_4VUDT8.js";
|
|
3
3
|
export { e as loadDotEnv, t as runStartCommand };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@voltro/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.24.0",
|
|
4
4
|
"description": "The `voltro` CLI — dev server, codegen, migrations, project scaffolding, agent-docs seeding, and production serve.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"voltro",
|
|
@@ -62,22 +62,22 @@
|
|
|
62
62
|
"@effect/platform-node": "^0.108.0",
|
|
63
63
|
"@effect/sql": "^0.52.0",
|
|
64
64
|
"@effect/workflow": "^0.19.0",
|
|
65
|
-
"@voltro/ai": "0.
|
|
66
|
-
"@voltro/cache": "0.
|
|
67
|
-
"@voltro/data-transfer": "0.
|
|
68
|
-
"@voltro/database": "0.
|
|
69
|
-
"@voltro/env": "0.
|
|
70
|
-
"@voltro/kv": "0.
|
|
71
|
-
"@voltro/logger": "0.
|
|
72
|
-
"@voltro/plugin-auth": "0.
|
|
73
|
-
"@voltro/plugin-broadcast": "0.
|
|
74
|
-
"@voltro/plugin-mail": "0.
|
|
75
|
-
"@voltro/plugin-storage": "0.
|
|
76
|
-
"@voltro/plugin-webhooks": "0.
|
|
77
|
-
"@voltro/protocol": "0.
|
|
78
|
-
"@voltro/runtime": "0.
|
|
79
|
-
"@voltro/serverless": "0.
|
|
80
|
-
"@voltro/workflow": "0.
|
|
65
|
+
"@voltro/ai": "0.24.0",
|
|
66
|
+
"@voltro/cache": "0.24.0",
|
|
67
|
+
"@voltro/data-transfer": "0.24.0",
|
|
68
|
+
"@voltro/database": "0.24.0",
|
|
69
|
+
"@voltro/env": "0.24.0",
|
|
70
|
+
"@voltro/kv": "0.24.0",
|
|
71
|
+
"@voltro/logger": "0.24.0",
|
|
72
|
+
"@voltro/plugin-auth": "0.24.0",
|
|
73
|
+
"@voltro/plugin-broadcast": "0.24.0",
|
|
74
|
+
"@voltro/plugin-mail": "0.24.0",
|
|
75
|
+
"@voltro/plugin-storage": "0.24.0",
|
|
76
|
+
"@voltro/plugin-webhooks": "0.24.0",
|
|
77
|
+
"@voltro/protocol": "0.24.0",
|
|
78
|
+
"@voltro/runtime": "0.24.0",
|
|
79
|
+
"@voltro/serverless": "0.24.0",
|
|
80
|
+
"@voltro/workflow": "0.24.0",
|
|
81
81
|
"chokidar": "^5.0.0",
|
|
82
82
|
"ioredis": "^5.11.1",
|
|
83
83
|
"tinyglobby": "^0.2.17",
|
package/templates/AGENTS.md
CHANGED
|
@@ -595,7 +595,7 @@ each plugin's own README.
|
|
|
595
595
|
|
|
596
596
|
| Topic | Open | Summary |
|
|
597
597
|
|---|---|---|
|
|
598
|
-
| **What's new in 0.
|
|
598
|
+
| **What's new in 0.23.0** | `node_modules/@voltro/cli/templates/agent-docs/whats-new.md` | Everything that changed in this version. Read it before hand-rolling something the framework may now ship. |
|
|
599
599
|
| AI | `node_modules/@voltro/cli/templates/agent-docs/ai.md` | How Voltro treats AI — agents, tools, streaming, RAG — all primitives over the same WebSocket as the rest of the framework. |
|
|
600
600
|
| Authentication | `node_modules/@voltro/cli/templates/agent-docs/authentication.md` | How @voltro/plugin-auth wires password + session-cookie auth across api + web, plus the pluggable identity-strategy protocol. |
|
|
601
601
|
| Caching | `node_modules/@voltro/cli/templates/agent-docs/caching.md` | Voltro's caching layer (@voltro/cache) — an always-on memory default, swappable Redis-compatible backends, a low-level wrap primitive, and automatic query-result invalidation. |
|
|
@@ -9,7 +9,7 @@ each plugin's own README.
|
|
|
9
9
|
|
|
10
10
|
| Topic | Open | Summary |
|
|
11
11
|
|---|---|---|
|
|
12
|
-
| **What's new in 0.
|
|
12
|
+
| **What's new in 0.23.0** | `node_modules/@voltro/cli/templates/agent-docs/whats-new.md` | Everything that changed in this version. Read it before hand-rolling something the framework may now ship. |
|
|
13
13
|
| AI | `node_modules/@voltro/cli/templates/agent-docs/ai.md` | How Voltro treats AI — agents, tools, streaming, RAG — all primitives over the same WebSocket as the rest of the framework. |
|
|
14
14
|
| Authentication | `node_modules/@voltro/cli/templates/agent-docs/authentication.md` | How @voltro/plugin-auth wires password + session-cookie auth across api + web, plus the pluggable identity-strategy protocol. |
|
|
15
15
|
| Caching | `node_modules/@voltro/cli/templates/agent-docs/caching.md` | Voltro's caching layer (@voltro/cache) — an always-on memory default, swappable Redis-compatible backends, a low-level wrap primitive, and automatic query-result invalidation. |
|
|
@@ -89,7 +89,7 @@
|
|
|
89
89
|
"group": "database.migrations",
|
|
90
90
|
"description": "Voltro's planner-based migration system — diff your declared schema against the live DB, classify each change, refuse-to-apply anything risky without explicit intent. Dev auto-applies, prod refuses.",
|
|
91
91
|
"path": "agent-docs/database/migrations.md",
|
|
92
|
-
"files":
|
|
92
|
+
"files": 17
|
|
93
93
|
},
|
|
94
94
|
{
|
|
95
95
|
"id": "database/misc",
|
|
@@ -166,6 +166,27 @@ import { handleSignIn } from '@voltro/plugin-auth'
|
|
|
166
166
|
|
|
167
167
|
Use `handleSignIn` instead of rolling your own — the timing-oracle gap is the kind of subtle bug that hides for years. Note it returns a `401 HandlerResult`, it does not throw a domain error.
|
|
168
168
|
|
|
169
|
+
## Brute-force lockout
|
|
170
|
+
|
|
171
|
+
`plugin-auth` locks an account after repeated failed credential attempts, so password-spraying and credential-stuffing don't get unlimited guesses. After **5 failed attempts** (a wrong password — or, for MFA users, a wrong second-factor code) within **15 minutes**, sign-in for that email is refused with a `429 account_locked` (carrying a `retryAfterSeconds`) for **15 minutes**. A completed login clears the counter.
|
|
172
|
+
|
|
173
|
+
The counter is keyed by **email**, not user id, and it tracks unknown addresses too: a locked account and an unknown-but-hammered address respond identically, so the lock can't be turned into an existence oracle — the same reasoning as the timing-oracle defence above.
|
|
174
|
+
|
|
175
|
+
It is **on by default** — a security default you get for free. Tune or disable it per app:
|
|
176
|
+
|
|
177
|
+
```ts
|
|
178
|
+
authRoutesPlugin({
|
|
179
|
+
store,
|
|
180
|
+
lockout: {
|
|
181
|
+
maxAttempts: 5, // failed attempts before locking (default 5)
|
|
182
|
+
windowSeconds: 900, // counting window (default 15 min)
|
|
183
|
+
lockSeconds: 900, // lock duration (default 15 min)
|
|
184
|
+
},
|
|
185
|
+
})
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
The counter lives in the `loginAttempts` table (contributed via `authTables`), so it appears automatically on your next `voltro db apply` / `voltro dev` boot. For lockout that holds across multiple nodes, back the store with Postgres (`postgresUserStore`) — the in-memory store is single-node.
|
|
189
|
+
|
|
169
190
|
## Rehashing on parameter bump
|
|
170
191
|
|
|
171
192
|
When the framework updates the default cost parameters, existing hashes stay valid — `verifyPassword` reads `N`/`r`/`p` from the stored hash string itself (they're encoded inline as `scrypt$<N>$<r>$<p>$…`). Rehash-on-verify ships: `needsRehash(stored)` reports whether a hash is below the current cost, and `verifyPasswordWithRehash(plaintext, stored)` returns `{ valid, rehash? }` — when the password matches an under-cost hash, `rehash` is a freshly-minted replacement. `handleSignIn` wires this through `UserStore.updatePassword`, so a user's stored hash silently strengthens on their next login, no forced reset and no backfill.
|
|
@@ -286,12 +286,13 @@ The matching `.<primitive>.server.ts` is **unchanged** — caching is a descript
|
|
|
286
286
|
|
|
287
287
|
## `scope` is required — and it's a security decision
|
|
288
288
|
|
|
289
|
-
`scope` has **no default**, because guessing wrong leaks one user's rows to another. The rubric is one question:
|
|
289
|
+
`scope` has **no default**, because guessing wrong leaks one user's rows to another. The rubric is one question, with **three** answers:
|
|
290
290
|
|
|
291
|
-
> **Does the resolved query depend on the caller?**
|
|
291
|
+
> **Does the resolved query depend on the caller — and on what about them?**
|
|
292
292
|
|
|
293
|
-
- **
|
|
294
|
-
- **
|
|
293
|
+
- **On the PERSON** — `where authorId = me`, anything row-scoped → **`scope: 'subject'`**. The cache key includes the caller's subject id, so two subjects can never share an entry.
|
|
294
|
+
- **On their ORG only** — an org-wide figure every colleague sees identically → **`scope: 'tenant'`**. One entry per `tenantId`, never shared across orgs.
|
|
295
|
+
- **On neither** — the same rows for everyone (reference / lookup data) → **`scope: 'global'`**. One entry shared across all callers.
|
|
295
296
|
|
|
296
297
|
```ts
|
|
297
298
|
// reference data — identical for everyone → global
|
|
@@ -302,9 +303,24 @@ export const listCountries = defineQuery({
|
|
|
302
303
|
output: Country,
|
|
303
304
|
cache: { ttl: '1h', scope: 'global' },
|
|
304
305
|
})
|
|
306
|
+
|
|
307
|
+
// an org-wide statistic — same for all 18 colleagues, never across orgs
|
|
308
|
+
export const last12Months = defineQuery({
|
|
309
|
+
name: 'globalStatistics.last12Months',
|
|
310
|
+
source: ['invoices', 'employees'],
|
|
311
|
+
input: Schema.Struct({}),
|
|
312
|
+
output: Stats,
|
|
313
|
+
cache: { ttl: '5m', scope: 'tenant' },
|
|
314
|
+
})
|
|
305
315
|
```
|
|
306
316
|
|
|
307
|
-
|
|
317
|
+
`'tenant'` exists because the other two were the only options and neither fit an org-wide figure: `'subject'` recomputes it per person — eighteen identical computations of the same nine-table statistic for an eighteen-person org — and `'global'` shares one entry across tenant boundaries, which for data derived from `subject.tenantId` is not a cache but a leak.
|
|
318
|
+
|
|
319
|
+
A caller with no `tenantId` (an anonymous or system subject) **bypasses** a `'tenant'` cache rather than sharing a null-keyed entry.
|
|
320
|
+
|
|
321
|
+
**Never put `scope: 'global'` on a subject- or tenant-filtered query.** Tenant tables are auto-scoped by the runtime, so a `global` cache over one would serve tenant A's rows to tenant B. **The boot audit checks this**: a `'global'` scope over a `tenant()`-scoped table is reported by `voltro dev` and refused under `VOLTRO_SERVER_ONLY=strict`. It stays silent for `'global'` on reference data — the case the option exists for — and for a query with no declared `source`, where it has nothing to reason about.
|
|
322
|
+
|
|
323
|
+
**`scope: 'tenant'` is not a replacement for modelling.** For a rollup, an aggregate with `tenantId` as an indexed column puts the tenant boundary in the *data* rather than in a cache key, which is better. `'tenant'` is for the other case: a query that must be FRESH and is merely expensive, where an aggregate's refresh interval is the wrong instrument.
|
|
308
324
|
|
|
309
325
|
## How auto-invalidation works
|
|
310
326
|
|
|
@@ -1069,6 +1069,21 @@ source counts as a guard, so `requireTeamAccess()` is recognised without any
|
|
|
1069
1069
|
configuration. Without that the scan would report every call site of your own
|
|
1070
1070
|
guards, which is the failure mode that makes a check ignorable.
|
|
1071
1071
|
|
|
1072
|
+
It reads your **whole source tree** for those names, not just the
|
|
1073
|
+
convention-named files — guards live in `lib/access.ts`, not in `*.mutation.ts`.
|
|
1074
|
+
The line above the counts tells you what it found, and it is worth reading before
|
|
1075
|
+
you trust the numbers:
|
|
1076
|
+
|
|
1077
|
+
```txt
|
|
1078
|
+
guard vocabulary: 17 from your source (requireTeamAccess, assertInquiryAccess, …)
|
|
1079
|
+
```
|
|
1080
|
+
|
|
1081
|
+
If it instead says `framework names only — no exported require*/assert* found in
|
|
1082
|
+
this app` while you know you export some, the counts below it are not meaningful:
|
|
1083
|
+
every call site of your own guards is being reported as unguarded. Check that
|
|
1084
|
+
they are `export`ed and that the name starts with `require` / `assert` followed
|
|
1085
|
+
by a capital.
|
|
1086
|
+
|
|
1072
1087
|
**An inline ownership check is informational.** `row.userId !== subject.id → new
|
|
1073
1088
|
AccessDeniedError({})` is correct code — it is listed so you can see where the
|
|
1074
1089
|
rule lives in a handler rather than on a descriptor, and it never fails the run.
|
|
@@ -2159,6 +2174,23 @@ voltro data restore ./backups/2026-07-01 # pg_restore / mysql / …
|
|
|
2159
2174
|
|
|
2160
2175
|
These shell out to the vendor tools resolved from your `DB_DIALECT` + connection env. They produce a dialect-native artifact (`db.dump`, `db.sql`, `db.sqlite`, `db.bacpac`) that is lossless and point-in-time consistent for **same-dialect** restore — the right tool for disaster recovery. Secrets are passed via the tools' environment variables (`PGPASSWORD`, `MYSQL_PWD`), never on the command line, where the tool supports it. The named tool must be installed and on `PATH`.
|
|
2161
2176
|
|
|
2177
|
+
### The provenance stamp — a restore that refuses the wrong DB
|
|
2178
|
+
|
|
2179
|
+
A native dump is opaque: it doesn't say which dialect made it, which schema shape it carries, or when. `backup` writes a sidecar `voltro-backup-stamp.json` next to the artifact recording exactly that — `dialect`, the live schema `fingerprint`, the `@voltro/cli` version, and the timestamp.
|
|
2180
|
+
|
|
2181
|
+
`restore` reads the stamp **before touching the DB** and acts on two failures that are otherwise silent until they corrupt:
|
|
2182
|
+
|
|
2183
|
+
- **Cross-dialect restore → refused.** Restoring a postgres dump while `DB_DIALECT=mysql` is never valid; it stops with an error instead of half-loading. Override with `--force` only if you genuinely know better.
|
|
2184
|
+
- **Schema/code skew → warned.** If the backup's schema fingerprint differs from what the running code declares, restore prints a warning to run `voltro db apply` afterwards — the dump's shape predates (or postdates) this deploy's code. (Production boot already refuses on a fingerprint mismatch; the stamp surfaces it at restore time, before the boot.)
|
|
2185
|
+
|
|
2186
|
+
A backup with no stamp (older, or hand-made) restores with a caution rather than a hard stop.
|
|
2187
|
+
|
|
2188
|
+
### Point-in-time recovery (PITR) is your database's job, not the framework's
|
|
2189
|
+
|
|
2190
|
+
`backup` is a point-in-time **snapshot**. "Restore to 14:32, just before the bad deploy" (PITR) needs continuous WAL/binlog archiving, which lives at the database/provider layer — pg's `archive_command` + a base backup (pgBackRest / WAL-G), a managed provider's continuous backup (RDS, Cloud SQL, Neon, PlanetScale). The framework deliberately does **not** reimplement it: layer PITR under these native snapshots at the infra layer. A weekly `voltro data backup` + provider PITR together give you both a portable artifact and a fine-grained restore point.
|
|
2191
|
+
|
|
2192
|
+
> **Test your backups.** A backup you've never restored is a hypothesis. Restore your latest artifact into a throwaway database and boot the app against it on a schedule — the stamp's dialect/fingerprint checks turn a silently-broken backup into a loud one, but only an actual restore proves the bytes are good.
|
|
2193
|
+
|
|
2162
2194
|
## Masking (prod → dev/stage safely)
|
|
2163
2195
|
|
|
2164
2196
|
Cloning prod into a lower environment must not carry real user data. `voltro data export`
|
|
@@ -2606,6 +2638,35 @@ voltro update --codemods-only --from 0.5.0 --to 0.6.0
|
|
|
2606
2638
|
|
|
2607
2639
|
Every `@voltro/*` entry in `dependencies` and `devDependencies` — in every workspace member, see above — with the range style preserved (`^0.3.0` stays caret, `~0.3.0` stays tilde) unless you pass `--exact`. Non-registry specs (`workspace:*`, `catalog:`, `link:`, …) are left untouched — they're already resolved by your monorepo or catalog.
|
|
2608
2640
|
|
|
2641
|
+
### And the peer dependencies the framework requires
|
|
2642
|
+
|
|
2643
|
+
`@effect/*` are **peer** dependencies, so your app declares them directly. When a
|
|
2644
|
+
release moves its peer range, bumping only `@voltro/*` leaves you installed
|
|
2645
|
+
against the old ones:
|
|
2646
|
+
|
|
2647
|
+
```txt
|
|
2648
|
+
Aligning peer dependencies the framework requires:
|
|
2649
|
+
@effect/rpc ^0.75.1 → ^0.76.0 (apps/api/package.json)
|
|
2650
|
+
@effect/platform ^0.96.2 → ^0.97.0 (apps/api/package.json)
|
|
2651
|
+
```
|
|
2652
|
+
|
|
2653
|
+
`update` reads those requirements off the freshly installed `@voltro/*` packages
|
|
2654
|
+
and re-installs if anything moved. Without it your package manager only *warns*,
|
|
2655
|
+
and the app compiles and boots on a graph the framework was never tested against
|
|
2656
|
+
— which is the failure mode with no symptom until there is one.
|
|
2657
|
+
|
|
2658
|
+
It is deliberately conservative:
|
|
2659
|
+
|
|
2660
|
+
- **Only peers you already declare.** One resolved transitively is not `update`'s
|
|
2661
|
+
to add.
|
|
2662
|
+
- **Only when your range is genuinely lower.** Pinned ahead, or pinned exactly at
|
|
2663
|
+
the floor (`0.76.0` vs `^0.76.0`), is left alone — that is a choice.
|
|
2664
|
+
- **Only ranges it can judge** (`^`, `~`, `>=`, exact). A union (`^1 || ^2`), a
|
|
2665
|
+
bounded range, `workspace:` / `catalog:` — untouched.
|
|
2666
|
+
|
|
2667
|
+
If two framework packages disagree about one peer, it says so and changes
|
|
2668
|
+
nothing: that is our bug, not yours to absorb silently.
|
|
2669
|
+
|
|
2609
2670
|
## When the install cannot run on this host
|
|
2610
2671
|
|
|
2611
2672
|
Some projects install in a container with their own store, from an offline
|