Summary
The Hola server reads multiInstance from manifest.json, but this repo's schema forbids it. Any bundle that declares the flag fails validate-catalog, so no app can opt into multiple instances — the flag added in try-hola/hola#246 is unreachable from the catalog.
The drift
Three facts that only bite in combination:
-
The server expects the field in the manifest. packages/server/src/services/core/catalog.ts:616:
const multiInstance = manifest.multiInstance === true ? true : undefined;
and it drives the singleton guard in packages/server/src/services/core/deployment.ts:2751:
if (multiInstance || allowMultiple) return;
// ...otherwise ConflictError: "This app is single-instance; pass --allow-multiple"
-
This repo's schema forbids it. schemas/manifest.schema.json is additionalProperties: false at root, and multiInstance is not among its 18 declared properties.
-
CI enforces the schema on every manifest. The validate-catalog job (.github/workflows/build-and-publish.yml:148-160) runs node bin/validate-manifest.mjs with no arguments, which discovers and validates every src/*/src/manifest.json on every PR and every push to main.
bin/validate-manifest.mjs passes --strict=false to ajv-cli, but that only relaxes schema-authoring strictness — additionalProperties is a validation keyword and is still enforced. A manifest carrying multiInstance: true fails with must NOT have additional properties.
Confirming the consequence: grep -rln multiInstance src/ returns nothing. Zero of 17 bundles declare it.
Impact
Every app in the catalog is single-instance, including the ones that most obviously shouldn't be. Running two Ubuntu Webtops today requires the --allow-multiple / "install another" override — the same deliberate escape hatch you'd use to force a second Vaultwarden. The server has a first-class way to express "multiples are expected here"; the catalog can't reach it.
Fix
Add multiInstance to the root properties of schemas/manifest.schema.json:
"multiInstance": {
"type": "boolean",
"description": "Whether multiple deployments of this app are expected. Default (omitted) is single-instance: the server rejects a second install unless the operator passes the per-install `allowMultiple` override. Declare true only when concurrent instances are genuinely independent — separate data roots and no shared external identity or singleton capability role."
}
Then set "multiInstance": true on the apps that warrant it.
Audit while we're in here
- webtop — the clear case. Independent
/config volumes, accepts: ["backup@1"] only, no provides/consumes/push.
- guacamole, running-man — plausible; worth a look.
- backrest — must stay single-instance. It declares
provides: ["backup@1"], whose provider grant is a read-only mount of every app's data (ADR 0004). Two providers is both ambiguous and a doubled blast radius.
- calibre-web — probably should not get the flag. Its
push target is mode: mirror, quiesce: stop, so hola app data push calibre-web library against the wrong instance deletes files. Multi-instance here needs a --deployment selector first.
- homepage —
consumes: "app-registry" is not a conflict (two readers are harmless), but low value.
Verification
node bin/validate-manifest.mjs should pass with the flag set on webtop, and a second webtop install should then succeed without --allow-multiple.
Related
A broader guard worth considering separately: the server's accepted manifest fields and this schema have no automated agreement check, which is how this drifted. #417 would add a second field across the same seam.
Summary
The Hola server reads
multiInstancefrommanifest.json, but this repo's schema forbids it. Any bundle that declares the flag failsvalidate-catalog, so no app can opt into multiple instances — the flag added in try-hola/hola#246 is unreachable from the catalog.The drift
Three facts that only bite in combination:
The server expects the field in the manifest.
packages/server/src/services/core/catalog.ts:616:and it drives the singleton guard in
packages/server/src/services/core/deployment.ts:2751:This repo's schema forbids it.
schemas/manifest.schema.jsonisadditionalProperties: falseat root, andmultiInstanceis not among its 18 declared properties.CI enforces the schema on every manifest. The
validate-catalogjob (.github/workflows/build-and-publish.yml:148-160) runsnode bin/validate-manifest.mjswith no arguments, which discovers and validates everysrc/*/src/manifest.jsonon every PR and every push to main.bin/validate-manifest.mjspasses--strict=falsetoajv-cli, but that only relaxes schema-authoring strictness —additionalPropertiesis a validation keyword and is still enforced. A manifest carryingmultiInstance: truefails withmust NOT have additional properties.Confirming the consequence:
grep -rln multiInstance src/returns nothing. Zero of 17 bundles declare it.Impact
Every app in the catalog is single-instance, including the ones that most obviously shouldn't be. Running two Ubuntu Webtops today requires the
--allow-multiple/ "install another" override — the same deliberate escape hatch you'd use to force a second Vaultwarden. The server has a first-class way to express "multiples are expected here"; the catalog can't reach it.Fix
Add
multiInstanceto the root properties ofschemas/manifest.schema.json:Then set
"multiInstance": trueon the apps that warrant it.Audit while we're in here
/configvolumes,accepts: ["backup@1"]only, noprovides/consumes/push.provides: ["backup@1"], whose provider grant is a read-only mount of every app's data (ADR 0004). Two providers is both ambiguous and a doubled blast radius.pushtarget ismode: mirror, quiesce: stop, sohola app data push calibre-web libraryagainst the wrong instance deletes files. Multi-instance here needs a--deploymentselector first.consumes: "app-registry"is not a conflict (two readers are harmless), but low value.Verification
node bin/validate-manifest.mjsshould pass with the flag set on webtop, and a second webtop install should then succeed without--allow-multiple.Related
allowMultipleoverrideminHolaVersion) hola#417 —minHolaVersion, the same apps/server manifest-contract seamA broader guard worth considering separately: the server's accepted manifest fields and this schema have no automated agreement check, which is how this drifted. #417 would add a second field across the same seam.