mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-11-01 15:35:12 +01:00
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - Managed Cloud instances must retain shared objects and secrets across server replacement. > - Implicit storage defaults can select local disk or an unintended bucket. > - Cloud runtime settings now come from a canonical database snapshot. > - This pull request validates bootstrap prerequisites before database setup and effective S3 settings after snapshot resolution. > - The benefit is an actionable refusal before local signing state or HTTP setup, while valid canonical Cloud settings work. ## Linked Issues or Issue Description **What happened?** A managed Cloud instance could start with local disk storage, an implicit bucket, or a local key file. After canonical Cloud configuration landed, checking only environment or file values would also reject a valid database bucket. Shared schema defaults could make an omitted raw bucket appear explicit. **Expected behavior** Managed Cloud requires external PostgreSQL, a stable environment master key, and effective S3 settings with a nonblank bucket. The canonical snapshot remains the configuration authority. Invalid configuration refuses startup before signing or HTTP effects. Self-hosted defaults remain compatible. **Steps to reproduce** 1. Select managed Cloud with its tenant token or managed configuration document. 2. Leave storage as local disk, or omit the S3 bucket from a raw canonical snapshot. 3. Start the server. Startup must refuse. An explicit valid bucket in the canonical snapshot must work without a file or environment bucket. **Paperclip version or commit** Current base: `4edd10804e2b42941aa2038e90c362c13a0b98dc`. **Deployment mode** Managed Cloud. Ordinary self-hosted defaults remain unchanged. Related PRs: #18335 owns canonical Cloud runtime configuration. #18327 owns S3 run logs. #18352 owns credential persistence. #18347 owns cutover checks. This PR adds configuration prerequisites to those existing boundaries. ## What Changed - Validate external PostgreSQL and a stable environment master key at the Cloud bootstrap boundary, before database setup. - Validate effective S3 settings after canonical snapshot publication and before Cloud signing, runtime consumers, or HTTP setup. - Require a nonblank bucket in the Cloud snapshot schema. Reject omitted or blank buckets in every legacy Cloud file before one-time import normalization, even when an environment bucket is supplied. Keep shared self-hosted schema defaults. - Preserve typed bucket provenance at the existing file-read boundary. Accept explicit canonical bucket names, including `paperclip`, and environment settings when the snapshot is null. - Validate effective legacy storage with the existing resolver before seeding. A rejected candidate leaves no canonical rows, so a corrected source file can be imported on retry. Environment overlays are checked but never persisted. - Add real PostgreSQL snapshot and migration-retry tests, startup refusal tests, and environment/file compatibility controls. Document that Cloud always requires its stable environment key, even with an external default secrets provider. ## Verification - Latest focused acceptance passed 199/199 in six files (54.29 seconds), using the command below plus `server/src/__tests__/cloud-configuration-migration-postgres.test.ts`. Real PostgreSQL proves rejected storage leaves all three migration authority rows absent, then a corrected legacy file imports on retry. An external-default-secrets control proves the deliberate unconditional Cloud master-key contract. - Before the peer correction, the frozen product diff passed 178 tests in five files: managed-cloud-prerequisites, cloud-runtime-config, server-startup-feedback-export, config-file, and cloud-configuration-readers. Command: `pnpm exec vitest run server/src/__tests__/{managed-cloud-prerequisites,cloud-runtime-config,server-startup-feedback-export,config-file,cloud-configuration-readers}.test.ts --maxWorkers=1 --no-file-parallelism`. Real PostgreSQL used a qualified POSIX test-only PG18.6 override because the host cannot supply the stock runtime's SysV semaphore allocation. - Coverage includes raw database and legacy omitted/blank bucket rejection, valid database and environment buckets, null snapshots, explicit default-named buckets, self-hosted compatibility, and refusal before local signing mutation. - The real AWS SDK configured-provider control uses a simulated loopback S3 service. It writes, reads from a fresh provider, and deletes an object. It also reads an encrypted credential with the stable environment key. This does not qualify production cloud permissions. - `git diff --check` passed. Final merges add unrelated master changes from #18241, #18363, and #18370; the focused storage product diff is unchanged. - Peer review reproduced a fresh legacy local-disk import that gained the default bucket before an S3 environment overlay. The importer now requires a raw explicit bucket for every legacy Cloud file. Six controls cover omission/blank values with and without an environment bucket; the full focused rerun passed 184/184 across all five files in 194.89 seconds. - Initial revised startup testing passed 167/168: one fixture omitted the database URL and failed before its intended key assertion. The corrected final run passed 178/178. An initial direct server typecheck failed because Runner declarations were not built; this is a setup failure, not a compile pass. Integration verification rebuilds dependencies before full checks. - Prior head `fae1e4aa` passed complete CI with 53 successes, two intended skips, and Greptile 5/5. `de71345f379cecb71dbfb219f35e14f942c10a3f` then passed complete CI (55 successes, three workflow skips, and a neutral external review), with Greptile 5/5 and zero unresolved threads. The external review identified the preseed rejection problem; its correction is committed at `75e04d0d77e19dcef0a8cb070b6b518f0f173b14`. The final merge head is `19b809ea2823a6a4e5ceb48ef2c2bac4721733d6`; [Fresh complete CI](https://github.com/paperclipai/paperclip/actions/runs/41127309762) passed on that head: 55 successful checks, three workflow skips, and one neutral external review. Greptile reviewed this exact head at 5/5 with no actionable issues. Both earlier external review threads are resolved with the tested fixes. Earlier infrastructure retries remain documented in [the first diagnosis](https://github.com/paperclipai/paperclip/pull/18350#issuecomment-6529964690) and [the runner shutdown evidence](https://github.com/paperclipai/paperclip/pull/18350#issuecomment-6530364769). Earlier missing SDK build and fixture metadata failures were repaired before those passes. - Current frozen-head CI first attempt failed one unchanged Antigravity authentication success case in Runner Vitest 1/2 (`outcome=failure`, 1994 tests passed). The same file passed 11/11 locally in 2.33 seconds, and identical Runner source passed the prior complete CI run. Attempt 2 retried only failed jobs (the Runner shard and dependent aggregate). Both passed without a source change. No second retry was used. The exact timing cause is not established. [Failed job](https://github.com/paperclipai/paperclip/actions/runs/41127309762/job/124309181778). - No production data, buckets, or live credentials were used. Complete local repository suites are not duplicated in this worktree; current-head required CI supplies the full suite gate. ## Risks - Managed deployments with local disk, omitted buckets, file-only keys, or missing external database URLs now refuse startup. Operators must supply valid prerequisites before rollout. - Required deployment environment overlays must remain stable across replacements. For example, a saved local-disk provider with an explicit bucket can use an environment S3 provider; removing that required overlay correctly refuses startup until the environment or canonical configuration is corrected. Environment settings are never silently copied into the canonical row. - Effective legacy storage must also pass before first seeding. A bad candidate does not create authority rows. Existing saved rows remain authoritative and can require environment overrides or a canonical configuration change if they are invalid. - A present legacy Cloud file needs an explicit raw bucket even if its saved provider is local disk or its environment supplies a bucket. Correct the file before one-time import, or use the existing absent-file/null-snapshot environment-only setup. Environment overlays are not persisted as operator choices. - A previously stored canonical nonblank `paperclip` bucket is authoritative. Prior normalization erased whether that value was originally explicit. This change cannot recover that provenance and does not reject existing nonblank canonical values. - Validation proves configuration shape, not cloud permissions, database reachability, or key compatibility with existing encrypted data. Keep the master key stable across replacements. - Cloud snapshot JSON parsing is stricter; omitted or blank raw S3 bucket values need correction. No database migration or provider operation changes are added. These prerequisites do not prove that every server persistence path is disposable. > For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and discuss it in `#dev` before opening the PR. Feature PRs that overlap with planned core work may need to be redirected — check the roadmap first. See `CONTRIBUTING.md`. ## Model Used OpenAI Codex, exact model ID `gpt-6.1-sol`. Context window: unknown. The model used reasoning, repository tools, code execution, and test execution. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes #` / `Refs #` OR (b) described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip` URLs) - [x] My branch name describes the change (e.g. `docs/...`, `fix/...`) and contains no internal Paperclip ticket id or instance-derived details - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [x] All Paperclip CI gates are green - [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [x] I will address all Greptile and reviewer comments before requesting merge --------- Co-authored-by: Paperclip <noreply@paperclip.ing>
142 lines
9.4 KiB
Markdown
142 lines
9.4 KiB
Markdown
# 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 and requires the stable environment encryption
|
|
master key. 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.
|
|
|
|
Managed Cloud always requires a stable environment master key, including when
|
|
an external default secrets provider is selected. Internal local-encrypted
|
|
credential paths still need that key.
|
|
|
|
Managed Cloud validates effective S3 storage after publishing the canonical
|
|
snapshot, before signing-key initialization, telemetry, authentication or app
|
|
construction. Cloud requires S3 and a nonblank bucket; an authoritative absent
|
|
configuration needs explicit environment storage settings. Raw saved snapshots
|
|
cannot obtain a bucket from a schema default. Every legacy Cloud file must name its
|
|
bucket before its one-time import, even if its saved provider is local disk and
|
|
an environment bucket is present. Omission or blank values fail before seeding;
|
|
The effective storage candidate is validated before seeding, so a rejected
|
|
legacy file leaves no canonical row and can be corrected before retry.
|
|
Environment overlays are not saved as operator choices. Correct that file before
|
|
import, or use the existing absent-file/null-snapshot environment-only setup.
|
|
Ordinary self-hosted file defaults remain unchanged.
|
|
Already-canonical nonblank values, including `paperclip`, remain authoritative.
|
|
Prior normalization cannot reliably distinguish an operator's deliberate name
|
|
from a historical default, so this release does not invent retrospective
|
|
provenance or replace those saved values.
|
|
|
|
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. Managed Cloud
|
|
performs that step after effective storage validation; self-hosted startup keeps
|
|
its pre-connection ordering. 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.
|