mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-11-04 19:28:46 +01:00
fix(cloud): persist configuration and load release-image packages (#18335)
Land the approved Cloud configuration and release-image package stack.
Includes the separately reviewed adapter settings (#18335), external adapters (#18333), bundled plugins (#18336), startup configuration (#18339), and migration/package checks (#18341).
Includes the final image-owned loader and linked-dependency inventory corrections.
Verified stack head: 462716170d.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
1 parent
36f4422389
commit
8f822bfcf7
57 files changed
+3670
-250
No files matched your search
@@ -722,6 +722,60 @@ connection, before enabling the new server and UI.
|
||||
- The repo keeps only the newest 5 snapshots. `generate` runs `prune:snapshots` afterwards to delete older ones. Drizzle only reads the newest snapshot, and each snapshot is a full copy of the schema (over 1 MB each). Older snapshots are still in git history.
|
||||
- `packages/db/src/migration-snapshot-drift.test.ts` is the enforcement backstop. It repeats the diff that `generate` performs and fails when the newest snapshot no longer matches `packages/db/src/schema/`.
|
||||
|
||||
## Managed adapter settings
|
||||
|
||||
Cloud-managed instances keep adapter selection preferences in the private
|
||||
`instance_settings` singleton `adapter-settings:v1`. Its `general` JSON contains
|
||||
`version: 1`, a monotonic `revision`, and `disabledTypes`. It is separate from
|
||||
the public `default` settings row and never appears in the instance settings API.
|
||||
Each update locks this row in a transaction. The update and company activity
|
||||
records commit together. Discovery and new-agent admission read the database
|
||||
at their asynchronous operation boundaries; no polling or mutable settings cache
|
||||
is used. Disabled adapters remain available to existing agents.
|
||||
|
||||
`PAPERCLIP_ADAPTERS` and `PAPERCLIP_ADAPTERS_FILE` declare the effective operator
|
||||
adapter list. The declaration is parsed once for a process configuration and
|
||||
validated at startup. It does not rewrite stored preferences. A conflicting
|
||||
adapter toggle returns 403. Removing the declaration restores saved choices.
|
||||
Operator file edits require a restart. Self-hosted instances continue to use
|
||||
`adapter-settings.json` for saved preferences.
|
||||
|
||||
The server-only adapter settings service exposes `inspectStored()` and
|
||||
`seedIfAbsent({ disabledTypes }, transaction?)` for migration tooling. Seeding
|
||||
uses the unique singleton key, returns `{ seeded, current }`, and never replaces
|
||||
an existing record. The caller can use the same transaction for its import
|
||||
receipt. Before admission, `initializeAdapterSettings` checks the durable row.
|
||||
When absent, it imports a valid legacy settings file once, retaining unknown
|
||||
adapter types, or seeds empty preferences if no file exists. It rejects files
|
||||
larger than 64 KiB, malformed input, and read failures. An existing durable row
|
||||
wins over every stale file. Ordinary Cloud reads do not read a legacy file.
|
||||
|
||||
The focused PostgreSQL suite can use an owned disposable PostgreSQL database via
|
||||
`PAPERCLIP_ADAPTER_SETTINGS_TEST_DATABASE_URL` when embedded PostgreSQL is
|
||||
unavailable. It applies migrations and inserts test data, so never point this
|
||||
variable at an existing application database.
|
||||
|
||||
## Cloud configuration migration receipt
|
||||
|
||||
The private `instance_settings` singleton `cloud-configuration-migration:v1`
|
||||
records atomic import completion, semantic source digests, imported revisions,
|
||||
and metadata-only external adapter blockers. It composes the adapter and
|
||||
startup-config owners without replacing an existing authoritative row. Receipt
|
||||
locking and the import transaction handle concurrent attempts and retries; no
|
||||
local completion marker participates in conflict resolution.
|
||||
|
||||
Qualification records current configuration revisions and selected image package
|
||||
evidence before normal plugin reconciliation. Image inspection does not hold
|
||||
admin write locks; configuration and registration snapshots are rechecked in a
|
||||
short transaction before the report is saved. A historical successful report
|
||||
never grants current eligibility. External metadata blockers survive loss of the
|
||||
legacy home and require a deliberate revision-checked recheck of a present valid
|
||||
original source file. Package mismatches remain recorded after maintenance
|
||||
reconciliation. See the [bounded migration and qualification procedure](cloud-configuration-migration.md)
|
||||
for the stopped-writer prerequisite, commands, retry semantics, integrity limits,
|
||||
and the explicit exclusion of secret bytes, whole-server zero downtime, and
|
||||
volume removal.
|
||||
|
||||
## Cloud runtime identity singleton
|
||||
|
||||
The private `instance_settings` row whose singleton key is
|
||||
@@ -1490,3 +1544,12 @@ It uses disposable embedded PostgreSQL by default. A disposable local database
|
||||
named `review_wait_benchmark...` can be supplied through
|
||||
`PAPERCLIP_TEST_DATABASE_URL` to compare another PostgreSQL major version.
|
||||
DataRow bytes exclude response metadata and transport overhead.
|
||||
|
||||
## Cloud runtime configuration singleton
|
||||
|
||||
The private `instance_settings` key `cloud-runtime-config:v1` holds a strictly
|
||||
validated, versioned nonsecret configuration and revision. It is separate from
|
||||
the ordinary instance settings API. Seed inserts preserve an existing winner;
|
||||
replacements lock the row and reject stale revisions. Database/transaction
|
||||
handles permit atomic migration receipts. Connection URLs, API keys and unknown
|
||||
fields are excluded. See [field ownership and startup order](cloud-runtime-configuration.md).
|
||||
+12
-1
@@ -1542,7 +1542,9 @@ Paperclip also persists an empty `OPENAI_API_KEY` override for those agents so a
|
||||
|
||||
If the `codex` CLI is not installed or not on `PATH`, `codex_local` agent runs fail at execution time with a clear adapter error. Quota polling uses a short-lived `codex app-server` subprocess: when `codex` cannot be spawned, that provider reports `ok: false` in aggregated quota results and the API server keeps running (it must not exit on a missing binary).
|
||||
|
||||
Local adapters require their corresponding CLI/session setup on the machine running Paperclip. External adapters are installed through the adapter/plugin flow and should not require hardcoded imports in `server/` or `ui/`.
|
||||
Local adapters require their corresponding CLI/session setup on the machine running Paperclip. Self-hosted instances can install external adapters through the adapter/plugin flow without hardcoded imports in `server/` or `ui/`.
|
||||
|
||||
Cloud-managed instances use only adapter implementations supplied by the release image. A tenant server token or the presence of `PAPERCLIP_MANAGED_CONFIG` enforces this boundary. Startup and UI parser requests do not read external adapter registrations or packages. Install, reinstall, reload, remove, and override operations return `403` with `adapter_install_platform_managed`, including for instance admins. Legacy `adapter-plugins.json` files stay untouched and do not change the built-in registry.
|
||||
|
||||
### Filesystem access boundary (2026-10-20)
|
||||
|
||||
@@ -3869,3 +3871,12 @@ in `_test` for `PAPERCLIP_MCP_SLEEP_TEST_DATABASE_URL`,
|
||||
`PAPERCLIP_DOT_RUNNER_TEST_DATABASE_URL`. The interaction-service regression suite
|
||||
also accepts `PAPERCLIP_INTERACTIONS_TEST_DATABASE_URL`. The suites migrate and write to those
|
||||
URLs; the sleep suite truncates company data between tests.
|
||||
|
||||
## Cloud startup configuration
|
||||
|
||||
Cloud stores nonsecret startup choices in a private PostgreSQL singleton.
|
||||
Bootstrap connection credentials stay in the supplied environment. A bounded
|
||||
one-time config-file seed preserves existing choices, then later starts use the
|
||||
database without loading home `.env` files or repairing worktree config. See
|
||||
[Cloud runtime configuration](cloud-runtime-configuration.md) for field ownership,
|
||||
secret handling, initialization order and migration interfaces.
|
||||
@@ -0,0 +1,148 @@
|
||||
# Cloud configuration migration and package qualification
|
||||
|
||||
This procedure covers saved adapter preferences, the allowlisted nonsecret
|
||||
startup configuration, and selected bundled plugin packages. The result is a
|
||||
point-in-time report. It does not authorize a deployment, guarantee zero
|
||||
downtime, establish general Railway admission, or prove that a volume can be
|
||||
removed. Secret and key bytes, database credentials, artifacts, logs, local
|
||||
storage, workspaces, and plugin state outside PostgreSQL need their own storage
|
||||
and replacement checks. This includes the decision signing key and local
|
||||
encryption keys. Their byte authority is unchanged by this procedure.
|
||||
Runtime environment and secret equality, plus an actual HTTP handoff between
|
||||
application processes, remain separate rollout prerequisites. The package digest
|
||||
does not establish those conditions.
|
||||
|
||||
## Migration prerequisite
|
||||
|
||||
Stop every legacy file writer before the first import. This includes old
|
||||
application replicas and operator commands that can write configuration or
|
||||
adapter settings. One maintenance window is sufficient. The importer cannot
|
||||
make an old file writer participate in its database transaction. Keep those
|
||||
writers stopped after import; later file edits do not update database authority.
|
||||
|
||||
Keep the normal database backup and recovery procedure for the instance. Use the
|
||||
new release with the same managed environment and database connection. The
|
||||
server runs migration before constructing configuration-dependent services.
|
||||
For an explicit source-checkout invocation, use:
|
||||
|
||||
```sh
|
||||
pnpm exec tsx server/src/qualify-cloud-configuration.ts --migrate
|
||||
```
|
||||
|
||||
The command uses `DATABASE_URL` and the normal Cloud environment. It does not
|
||||
accept credentials as arguments. It writes the receipt to PostgreSQL and prints
|
||||
a JSON report; no report file or local completion marker is required. Exit code
|
||||
0 means the requested configuration/package check passed, 2 means the report has
|
||||
blockers, and 1 means the operation failed. A fresh installation can report a
|
||||
missing plugin registration before normal boot installation completes. Run the
|
||||
check again after that installation settles.
|
||||
|
||||
## Database authority and retry behavior
|
||||
|
||||
The private `instance_settings` singleton `cloud-configuration-migration:v1`
|
||||
contains the versioned receipt. The transaction locks that row and composes the
|
||||
adapter and startup-config owners' atomic seed methods. Completion and both
|
||||
seeds commit together. A failed write or crash before commit leaves no completed
|
||||
receipt. A retry uses committed database state and never replaces a later
|
||||
settings revision. Earlier independently seeded owner records remain authoritative.
|
||||
|
||||
The receipt records source kind, a digest of the nonsecret semantic candidate,
|
||||
the imported revision, and bounded legacy external-registration evidence.
|
||||
An absent startup configuration remains an authoritative `config: null`; it is
|
||||
distinct from an uninitialized database record. No raw secret values are copied.
|
||||
Once the receipt exists, ordinary boots do not reread legacy source files.
|
||||
A replacement home can be empty or absent without changing saved authority.
|
||||
|
||||
All legacy readers have a 64 KiB limit. They require regular files, reject
|
||||
symlinks and FIFOs, and use bounded reads. External registration inspection reads
|
||||
metadata only. It never loads a registered module or follows a registered package
|
||||
path. A nonempty, invalid, or unreadable registration file creates a durable
|
||||
blocker. Removing the old home does not clear that blocker.
|
||||
|
||||
To resolve an external-registration blocker, stop its writer, deliberately
|
||||
remove the unsupported registration metadata at the original source location,
|
||||
and leave a present valid empty JSON array file. Read the current receipt revision
|
||||
from the report, then run:
|
||||
|
||||
```sh
|
||||
pnpm exec tsx server/src/qualify-cloud-configuration.ts --recheck-external-revision 3
|
||||
```
|
||||
|
||||
Use the actual current revision in place of `3`. A changed revision, a different
|
||||
source location, or a missing/invalid source file rejects the recheck. Reusing
|
||||
the same container path with an empty home is insufficient. This is an explicit
|
||||
operator resolution; qualification never clears the blocker automatically.
|
||||
|
||||
## Current packages and overlap comparison
|
||||
|
||||
Every qualification reads current settings revisions and plugin registrations.
|
||||
It resolves selected packages from this image with the same bundled package
|
||||
identity rules as normal loading. It verifies package bytes before importing a
|
||||
trusted image manifest, then compares that manifest with the approved database
|
||||
registration. Missing packages, missing evidence, incompatible versions,
|
||||
unselected registrations, and integrity failures prevent qualification. Legacy
|
||||
package paths and npm discovery cannot supply missing image bytes.
|
||||
|
||||
Image inspection happens outside database write locks. A short transaction then
|
||||
locks and rereads the current configuration and registration records. A change
|
||||
during inspection produces a blocker and requires a new check. The report gives
|
||||
`adapterRevision`, `configRevision`, `configurationDigest`, and
|
||||
`packageEvidenceDigest`. A historical completed receipt is evidence, not current
|
||||
authorization. Call qualification again for a current result.
|
||||
|
||||
The server records this report before ordinary bundled-plugin reconciliation.
|
||||
It retains `lastPackageMismatch` after later successful checks, so reconciliation
|
||||
cannot erase the evidence seen before an upgrade. The report does not block
|
||||
normal maintenance upgrades or replace capability approval. After an upgrade
|
||||
settles, its current packages can qualify normally.
|
||||
|
||||
For a same-package comparison, capture the current release's
|
||||
`packageEvidenceDigest` and pass it explicitly on the replacement:
|
||||
|
||||
```sh
|
||||
pnpm exec tsx server/src/qualify-cloud-configuration.ts --expected-package-evidence sha256:REPLACE_WITH_THE_64_HEX_DIGEST
|
||||
```
|
||||
|
||||
`configurationPackagesEligible` describes current database/configuration/package
|
||||
compatibility. `samePackageOverlapEligible` is true only when an explicit expected
|
||||
digest also matches. There is no permanent first-release baseline that would
|
||||
prevent future maintenance releases from establishing their own evidence.
|
||||
|
||||
The package evidence digest covers selected packages, not container image bytes.
|
||||
Distribution packages reuse and reverify their catalog digest. Builtin packages
|
||||
have a bounded inventory of physical file bytes, modes, and logical symlink
|
||||
targets, including standalone pnpm dependency trees. With the selected catalog
|
||||
root, inventory may follow links only into that provider, the catalog SDK,
|
||||
`packages/shared`, and the current image's root `node_modules`. Reached pnpm
|
||||
package units include their sibling dependency links; unrelated root packages
|
||||
are not scanned. Canonical directory/file deduplication bounds cycles, and
|
||||
logical root labels preserve the digest when identical image bytes relocate.
|
||||
Dangling or escaping links, special files, more than 50,000 entries, depth above
|
||||
32, metadata above 128 KiB, or more than 256 MiB per package fail verification.
|
||||
The measured cached Cloud Daytona closure has about 22,253 entries and 130 MB;
|
||||
that artifact predates this change and is not a complete current release proof.
|
||||
Inventory work is synchronous and linear in selected package bytes. It runs only during startup before the application is constructed
|
||||
or in the explicit operator process, with no idle polling. The report includes
|
||||
`packageInspectionDurationMs`. Packages must remain immutable throughout a check.
|
||||
|
||||
## Verification scope
|
||||
|
||||
The focused PostgreSQL tests use two independent clients and separate home/image
|
||||
roots. They import legacy settings, change saved revisions, remove the old roots,
|
||||
reconstruct from the database, and start a real plugin worker from the replacement
|
||||
image fixture. They also cover concurrent migration, commit failure and retry,
|
||||
database failure, stale sources, durable external blockers, package mismatch,
|
||||
maintenance reconciliation, and changes during manifest inspection.
|
||||
|
||||
The bundled-plugin registration suite also has an actual prebuilt Daytona case.
|
||||
Build its standalone package with the normal SDK linker before running that case;
|
||||
it is skipped when the provider build is absent. It qualifies the real linked
|
||||
SDK/shared package graph and uses the production loader and worker manager for
|
||||
credential-free initialize/health calls. The runtime-path tests also invoke the
|
||||
production server bundler to verify its preserved module locations.
|
||||
|
||||
Set `PAPERCLIP_CLOUD_CONFIGURATION_TEST_DATABASE_URL` only to an owned disposable
|
||||
PostgreSQL database when embedded PostgreSQL is unavailable. The suite applies
|
||||
migrations and writes/deletes test records. A complete release image, hosted
|
||||
replacement, overlapping production processes, secret-byte migration, and volume
|
||||
removal are not established by these local fixtures.
|
||||
@@ -0,0 +1,118 @@
|
||||
# Cloud runtime configuration
|
||||
|
||||
Cloud-managed servers store nonsecret startup and application choices in the
|
||||
private `instance_settings` singleton `cloud-runtime-config:v1`. The row contains
|
||||
`version: 1`, an increasing `revision`, and a strict allowlist of configuration
|
||||
fields. It is separate from the normal instance settings row and is not exposed
|
||||
by the instance settings API.
|
||||
|
||||
The canonical managed-instance predicate applies: a nonblank Cloud tenant token
|
||||
or the presence of `PAPERCLIP_MANAGED_CONFIG`, including a blank value, selects
|
||||
this configuration path. Invalid managed feature documents still retain their
|
||||
existing validation at application startup.
|
||||
|
||||
## Startup and authority
|
||||
|
||||
1. The hosting environment supplies `DATABASE_URL` and, if needed,
|
||||
`DATABASE_MIGRATION_URL`. Cloud requires a PostgreSQL URL and never falls back
|
||||
to embedded PostgreSQL. Credentials, trust keys, provider configuration and
|
||||
deployment wiring needed before the database connection remain supplied by
|
||||
the environment or the existing secret system. This phase parses only the
|
||||
database connection record. Runtime mode, host binding and authentication
|
||||
checks wait until the saved snapshot has loaded.
|
||||
2. After schema migration and database connection, startup reads the private
|
||||
runtime configuration row. An existing invalid row or a database error stops
|
||||
startup. Neither condition selects a legacy file or an old process snapshot.
|
||||
3. If no row exists, startup imports the legacy config once. It uses
|
||||
`PAPERCLIP_CONFIG` when supplied, otherwise the instance home config path.
|
||||
It does not search working-directory ancestors. A truly absent file saves
|
||||
an authoritative `config: null` marker. This retains the existing dynamic
|
||||
environment/image defaults, including authentication and secret strictness.
|
||||
The marker never enables a later file fallback. A present malformed, linked, nonregular, or oversized file
|
||||
stops initialization. Reads and stored snapshots are limited to 64 KiB.
|
||||
4. The seed uses an insert that does not replace an existing row. Concurrent
|
||||
initializers read the committed winner. Later starts do not inspect the
|
||||
legacy file, even when it is absent, corrupt, or changed.
|
||||
5. Startup publishes a detached configuration snapshot, restores the signed
|
||||
Cloud runtime identity, then initializes telemetry, secret-provider defaults,
|
||||
authentication and application services. A saved telemetry opt-out therefore
|
||||
takes effect before a telemetry client can be created.
|
||||
|
||||
Existing operator environment overlays keep precedence over saved values.
|
||||
Reading an overlaid value does not save it back to the database. Credentials and
|
||||
connection URLs are never copied into this JSONB row.
|
||||
|
||||
Cloud does not load instance or working-directory `.env` files and does not run
|
||||
legacy worktree configuration repair. The hosting system must supply those
|
||||
bootstrap variables directly. In particular, a database connection kept only
|
||||
in `.env` cannot bootstrap this path. Self-hosted configuration and dotenv
|
||||
behavior remain unchanged.
|
||||
|
||||
## Field ownership
|
||||
|
||||
The table lists every field consumed from the existing config file and the
|
||||
remaining known schema fields. Environment-only `Config` fields, such as
|
||||
heartbeat maintenance flags and feedback-export credentials, retain their
|
||||
existing environment sources and are not frozen into the saved snapshot.
|
||||
|
||||
| Config file fields | Cloud owner |
|
||||
| --- | --- |
|
||||
| `server.deploymentMode`, `exposure`, `bind`, `customBindHost`, `host`, `port`, `allowedHostnames`, `serveUi` | Saved runtime configuration, with existing environment overlays. |
|
||||
| `auth.baseUrlMode`, `publicBaseUrl`, `disableSignUp` | Saved runtime configuration, with environment overlays and the signed Cloud identity restoration. Saved URLs cannot contain userinfo, query parameters, or fragments. |
|
||||
| `database.backup.enabled`, `intervalMinutes`, `retentionDays`, `dir` | Saved runtime configuration, with existing backup environment overlays. |
|
||||
| `database.mode`, `connectionString` | Cloud bootstrap requires the environment `DATABASE_URL`. The legacy URL is never saved. |
|
||||
| `database.embeddedPostgresDataDir`, `embeddedPostgresPort` | Local embedded-database settings. Cloud does not use embedded PostgreSQL or these paths. |
|
||||
| `storage.provider`, `localDisk.baseDir`, `s3.bucket`, `s3.region`, `s3.endpoint`, `s3.prefix`, `s3.forcePathStyle` | Saved runtime configuration, with existing environment overlays. Saved endpoints cannot contain userinfo, query parameters, or fragments. |
|
||||
| `secrets.provider`, `strictMode`, `localEncrypted.keyFilePath` | Saved nonsecret provider choice and locator, with existing environment overlays. No key bytes are imported or saved. |
|
||||
| `telemetry.enabled` | Saved choice, passed to the existing telemetry initialization and opt-out policy before client creation. |
|
||||
| `llm.provider` | Saved provider metadata. |
|
||||
| `llm.apiKey` | Never saved. The existing OpenAI model-discovery consumer uses `OPENAI_API_KEY` first. A legacy OpenAI key requires that environment credential before import, or startup stops. Claude key metadata has no server consumer. |
|
||||
| `logging.mode`, `logDir`, `updates.checkEnabled` | Nonsecret legacy/CLI metadata is preserved. Server logging continues to use its existing environment configuration. |
|
||||
| `$meta` | CLI file-write metadata is not application authority. The compatibility view synthesizes valid metadata. |
|
||||
| Unknown extension keys | No server config consumer exists. They are excluded from the strict saved allowlist, including any unknown credential fields. |
|
||||
|
||||
A saved path is a locator. This change does not make local storage objects,
|
||||
backup bytes, logging files, or secret-key bytes durable. Their storage owners
|
||||
and existing environment/secret mechanisms retain responsibility for those
|
||||
bytes. Restart tests prove configuration restoration under a different home;
|
||||
they do not prove durable file, secret, or backup storage.
|
||||
|
||||
The decision signing service still uses `PAPERCLIP_DECISION_SIGNING_SECRET` when
|
||||
supplied and otherwise reads or creates its instance key file before the database
|
||||
connection. The local encrypted secret provider still uses its environment key
|
||||
or existing key-file mechanism. Those byte-storage fallbacks are outside this
|
||||
configuration migration. Qualification must account for them separately; this
|
||||
PR does not establish file-free secret storage on Cloud.
|
||||
|
||||
## Internal update and migration interface
|
||||
|
||||
`cloudRuntimeConfigService(db)` provides `inspectStored(handle?)`,
|
||||
`seedIfAbsent(config, handle?)`, and `replace(config, expectedRevision, handle?)`.
|
||||
Each method accepts a database or transaction handle. A migration can compose a
|
||||
config seed, related settings and a receipt in one transaction.
|
||||
|
||||
Replacement validates the same allowlist, locks the singleton row, checks the
|
||||
expected revision, and increases it. A stale revision fails. There is no new
|
||||
public configuration endpoint or settings UI. Startup consumers use a snapshot;
|
||||
a saved replacement takes effect on the next initialization/restart.
|
||||
|
||||
`readLegacyCloudRuntimeConfig(env?)` returns the validated nonsecret candidate
|
||||
without writing it. `initializeCloudRuntimeConfig` performs the standalone
|
||||
one-time seed and publishes its committed snapshot. Integrated migration
|
||||
receipts and deployed Cloud qualification are separate consumers of this
|
||||
interface.
|
||||
|
||||
## Verification
|
||||
|
||||
`server/src/__tests__/cloud-runtime-config.test.ts` exercises real PostgreSQL
|
||||
clients, concurrent writes, transaction rollback, changed homes, a fresh Node
|
||||
process, environment precedence, secret exclusion, invalid data and database
|
||||
failure. It uses embedded PostgreSQL by default. A dedicated disposable database
|
||||
can be selected with `PAPERCLIP_RUNTIME_CONFIG_TEST_DATABASE_URL` when the host
|
||||
cannot start embedded PostgreSQL. That database must be reserved for this test.
|
||||
The test applies the current schema before use.
|
||||
|
||||
The startup orchestration controls in
|
||||
`server-startup-feedback-export.test.ts` hold configuration initialization and
|
||||
verify that telemetry, authentication and app construction wait for it. These
|
||||
local controls do not claim a live Cloud rollout.
|
||||
@@ -34,6 +34,42 @@ Current limitations to keep in mind:
|
||||
|
||||
In practice, that means the current implementation is a good fit for local development and self-hosted persistent deployments, but not yet for multi-instance cloud plugin distribution.
|
||||
|
||||
### Managed image packages
|
||||
|
||||
Managed Cloud provisions only plugins selected from the current release image
|
||||
catalog. A Cloud tenant token or managed configuration keeps this image-only
|
||||
policy active. Without an explicit selected package list, Cloud selects no
|
||||
packages and cannot fall back to local installs. An explicit operator-managed
|
||||
list also keeps this policy active outside Cloud. The database registration's
|
||||
plugin key, package name, version and
|
||||
approved manifest identify that package. Its legacy `packagePath` remains for
|
||||
local/self-hosted compatibility; Cloud never uses that path to locate code.
|
||||
Workers, plugin migrations and UI files resolve through the same current-image
|
||||
package resolver. Cloud does not install npm dependencies, build packages, use
|
||||
the development UI proxy, or fall back to a server home directory.
|
||||
|
||||
Prebuilt workers use the current release's baked CLI `tsx` loader for the SDK's
|
||||
workspace TypeScript dependencies. The loader is located from the owning server
|
||||
package in both regular and bundled server layouts. Missing loaders and loader
|
||||
links outside the release's CLI or root `node_modules` fail activation. This
|
||||
loads image-supplied code without installing dependencies or building packages.
|
||||
|
||||
Equivalent packages at different image roots reuse the existing registration,
|
||||
configuration, approval status and company scopes without rewriting its stored
|
||||
path. Concurrent first registration of the same package retains one plugin ID.
|
||||
Conflicting package identities fail explicitly. Normal maintenance boot retains
|
||||
the existing bundled manifest/version reconciliation and approval gates.
|
||||
|
||||
`createBundledPluginPackageResolver(...).inspect(registration, verifiedManifest)`
|
||||
is a read-only qualification hook. Resolve the current image path, load its
|
||||
manifest with the validated plugin loader, and inspect the registration before
|
||||
normal boot reconciliation. Missing manifest evidence and incompatible packages
|
||||
produce explicit blockers. Distribution packages retain their catalog digest
|
||||
verification; built-in packages trust the immutable release image and must bump
|
||||
their version when their code changes. Qualification supports identical package
|
||||
sets only. This package-resolution boundary does not coordinate workers,
|
||||
background jobs, or deployment overlap between servers.
|
||||
|
||||
## 1. Scope
|
||||
|
||||
This spec covers:
|
||||
|
||||
Reference in new issue
Block a user