Repository navigation
Description
Filing gate: ① a measured product defect with reach: every browser file upload on hosted production. Filed on the maintainer's direct word in the cloud ops seat's chat, 2026-10-26: 「另在 objectstack 立卡让 CSP 自动带上存储端点」.
Reader: the domain:server / CLI seat that owns packages/cli/src/utils/console.ts (the Console CSP), dispatch step: one PR.
Dedupe:
- REST
search/issues,repo:objectstack-ai/objectstack OS_CONSOLE_CSP_CONNECT_SRC: 4 hits including closed. The nearest are feat(cli): serve the Console document with a nonce-based content-security-policy #25569 (closed: introduced the CSP), map view: a deployment-wide default map style (setting + runtime-config + Console CSP), so an intranet deployment can replace demotiles.maplibre.org #26001 / feat(cli,cloud-connection): OS_CONSOLE_MAP_STYLE_URL names the deployment's default map style #26162 (closed: the map-style origin) and cloud: check that objectos-runtime serves the Console andGET /api/v1/runtime/configwith the sameOS_CONSOLE_MAP_STYLE_URL, and list the variable in cloud's deployment env reference (the cloud half of #26001) #26164 (open: cloud's map-style env check). None covers the storage endpoint. repo:objectstack-ai/objectstack csp presigned upload: 0 hits.
维护者速读
#25569 给 Console 文档加了 nonce CSP,connect-src 只有 'self'、默认地图源,以及运维在 OS_CONSOLE_CSP_CONNECT_SRC 里写的来源。但 S3/R2 存储的上传是浏览器直接 PUT 到预签名 URL,指向存储端点的主机。这个主机不在 connect-src 里,浏览器就拒绝请求(TypeError: Failed to fetch),于是附件上传整条坏掉。
框架自己知道存储端点(OS_STORAGE_S3_ENDPOINT,以及适配器给出的预签名主机),CSP 应该自动把它加进 connect-src,不该要求每个部署再手配一个变量。
Measured (2026-10-26)
Production jack.objectos.ai (cloud v2.5.0 ef4c5d2d, framework pin c475b45b56):
POST /api/v1/storage/upload/presigned→ 200,uploadUrlonhttps://<account>.r2.cloudflarestorage.com/objectos-attachments-prod/projects/<env>/user/<id>.txt, method PUT;- the browser's
PUTto that URL →TypeError: Failed to fetch; - the document's CSP reads
connect-src 'self' https://demotiles.maplibre.org; - a curl preflight to the same bucket with
Origin: https://jack.objectos.aianswers 204 withAccess-Control-Allow-Origin: https://jack.objectos.ai. So the bucket's CORS allows it, and the page's CSP is what refuses.
Staging os-4e4qa4.objectos.app (cloud 3689c6f6, same pin): the same failure.
The same round trip passed on 2026-10-25 at cloud b64d2cbf, whose framework pin predates #25569 (presigned 200 → PUT 200 → complete 200 → download 200, bytes back; objectstack-ai/cloud#2767 step (d)).
Hosted path: cloud's objectos-runtime mounts the Console through @objectstack/cli/console (mountConsoleStatic), which reads OS_CONSOLE_CSP_CONNECT_SRC once at boot (readExtraConnectSources).
Change
- When the deployment's storage serves uploads through presigned URLs, add its endpoint origin to the Console CSP
connect-srcat boot. Read it from the same resolved storage settings the storage service builds its adapter from (theOS_STORAGE_S3_ENDPOINTorigin, or the adapter's own presign host). Don't keep a second copy of the endpoint rule. - If the endpoint is virtual-hosted (bucket in the host name) or region-templated, add the exact origin the presigner emits.
OS_CONSOLE_CSP_CONNECT_SRCstays for operators' extra sources.- Tests:
- with S3 storage configured, the served CSP's
connect-srccontains the endpoint origin; - with local storage, it adds nothing;
- a parity test that the CSP origin equals the origin of a real presigned URL from the adapter.
- Ablate the addition.
- with S3 storage configured, the served CSP's
Acceptance
- The tests above.
- On a hosted environment with S3/R2 storage and no
OS_CONSOLE_CSP_CONNECT_SRC: the browser upload round trip (presigned → PUT → complete → download) succeeds and the console logs no CSP violation.
Interim (hosted)
The cloud ops seat is setting OS_CONSOLE_CSP_CONNECT_SRC to the R2 origin on both cloud planes as a stopgap. Once this ships, it can be removed.
Clause-②: no (widens what the document lets the page reach, to a host the server itself hands the page). The seat confirms.
Metadata-protocol: none
Activity
objectstack-fleet commentedon Oct 26, 2026
Triage: first grade —
bug·priority:p1·domain:server·area:files·pm:queue· triage seat ·session_01AavokzJ5DndAwitDXvKy4U· 2026-10-26T16:05Z⛔ Not a claim, ⛔ not a dispatch.
connect-srclacks the presign endpoint). This was filed on the maintainer's own word (「另在 objectstack 立卡让 CSP 自动带上存储端点」).OS_CONSOLE_CSP_CONNECT_SRCis the stop-gap on hosted, which cloud can set now.Generated by Claude Code
objectstack-fleet commentedon Oct 26, 2026
Claim: PM loop round 8 · card thread
Session:
session_01R1q2xdtYgU6FKrZ7LgjPCC(the seat); the card thread issession_01JFi7QBsDBsBeLzDyTyerZhAccount:
os-project-manager(the seat's linked user; the card's assignee)Branch:
claude/project-thread-vldk8b(the card thread's harness-pinned branch; it names no issue, so this claim is the identity record)Worktree: the card thread's own container checkout
Domain:
domain:serverSeat:
domain:server#1Ruling-ref: the maintainer's 「另在 objectstack 立卡让 CSP 自动带上存储端点」, quoted on the card, and triage first grade (2026-10-26T16:05Z, p1). Add only the configured storage endpoint's origin, exactly as the presigner emits it, derived from the storage settings the adapter is built from. No second copy of the endpoint rule, no wildcard. An unset or invalid endpoint, or a local driver, adds nothing.
OS_CONSOLE_CSP_CONNECT_SRCstays.File surface:
packages/cli/src/utils/console.ts(Node built-ins only; the origin reaches it from its caller), the CLI serve/mount call site, the storage service or its adapter (a presign-origin accessor), and their tests. A cloud call-site half, if any, is reported for a cloud-lane card. ⛔ Nopackages/spec/**(stop on breach; explain in the report)Container & model:
S,mode:thread, default model and effort (set at thread open)Clause-②: no — provisional (the CSP widens only to a host the server itself hands the page); the dev measures it on the diff
Metadata-protocol: none
Responsibility: every browser upload to S3/R2 on hosted production fails with
Failed to fetchsince #25569 (connect-srclacks the presign host)Thread-read: none
Premise: holds (read 2026-10-26T17:28Z at
origin/main3d50a43bcb: card open, unassigned,pm:queue, no claim;console.tsbuildsconnect-srcfrom'self', the map-style origin andOS_CONSOLE_CSP_CONNECT_SRConly)Serial constraints cleared: no open PR touches
packages/cli/src/utils/console.ts; the dev re-measures beforepr_createobjectstack-fleet commentedon Oct 26, 2026
os-dev-report
{ "issue": 26200, "status": "done", "branch": "claude/project-thread-vldk8b", "pr": "https://github.com/objectstack-ai/objectstack/pull/26281", "head": "1f201e46b45eb9399c22d0aad7d5b11175ecd2f1", "session": "session_01JFi7QBsDBsBeLzDyTyerZh", "premise_still_valid": true, "summary": "S3StorageAdapter.getPresignedUploadOrigin() signs a probe PutObject for its own bucket with the same client options every real presign uses (one private clientOptions(), fixed fake credentials, no network, no credential chain) and answers that URL's origin, so virtual-hosted, region-templated, path-style and dotted-bucket hosts come out exactly as the presigner emits them; SwappableStorageService forwards it for the active adapter (undefined for local). The Console mount (console.ts, still Node built-ins only) reads it off the kernel's `storage` service by name PER DOCUMENT and adds it to connect-src after the error-reporting origin and before OS_CONSOLE_CSP_CONNECT_SRC; a Setup settings swap is followed on the next document with no restart. No storage service, local storage, an endpoint the SDK refuses, or an answer outside the module's connect-source grammar adds nothing (the last two warn once). Deviation from the order's mechanism hypothesis: no serve.ts / createConsoleStaticPlugin option was added; reading the kernel service needs no call-site change on the hosted caller, keeps the public console face unchanged, and covers runtime settings changes. Reading taken on 'endpoint unset': AWS S3 with no custom endpoint still adds its region-templated origin (the ruling names region-templated hosts, which exist only without an endpoint); 'unset' read as no S3 store configured.", "tests": "Red first (base 3d50a43bc + new cases): CSP suite 4 failed / 40 passed, each storage case read connect-src 'self' https://demotiles.maplibre.org only. Green: cli CSP suite 45/45; service-storage s3-storage-adapter.presign-origin.test.ts 10/10. Ablation at head 0fd3cd0d via scripts/ablation-replace.mjs (console.ts imported from source, no dist leg): deleting the storageUploadOrigin push landed (anchor 1->0, blob 497ea91966dc->1f54438adfbc), CSP suite 4 failed / 41 passed, restore blob == HEAD and git diff HEAD empty. Head 0fd3cd0d: full cli unit project 334 files / 4884 tests passed (integration tier declared to CI; no spawn entry touched). After merging origin/main (head 8990880046, main touched service-storage): service-storage test 67 files / 1236 tests passed; service-storage and cli typecheck exit 0; CSP suite + test/published-subpath-console.pin.test.ts 2 files / 59 tests passed. eslint --no-inline-config over the six touched files exit 0.", "gates": "dispatch-gates --commands derived 72 at 0fd3cd0d; every exit captured by redirect. Three first exited 3 (prerequisite: shallow clone for check-plugin-teardown-shape --self-test; unbuilt packages outside the diff for check:dual-build-cjs-loads and check:i18n-coverage); rerun after unshallow/build, all exit 0. --ran: 72 derived, 72 run, 0 NOT-MEASURED, 0 UNRUN, exit 0. At merged head 8990880046 (derivation = same list less the two check-position-name-fold-loaders rows main retired): check:type-check-coverage, check:type-check-debt, check:query-options-erasure, check:test-source-alias, check:cross-package-test-inputs, check:nul-bytes exit 0. Final head 1f201e46b4 (changeset wording only): check-changeset-no-major, check-adr-0087-registration, check-empty-changeset, check:nul-bytes exit 0. PR check-runs: in_progress at report time (not waited on).", "declarations": "Clause-②: yes (widening) — measured: two new public methods on exported classes of a published package (S3StorageAdapter#getPresignedUploadOrigin, SwappableStorageService#getPresignedUploadOrigin) plus one more accepted connect-src origin; the claim's provisional 'no' did not hold on the diff. Metadata-protocol: none. patch changeset for @objectstack/cli and @objectstack/service-storage. No packages/spec path.", "dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm", "mcp_calls": "0 — no MCP GitHub calls; reads went through gh REST GETs", "api_writes": "2 relay strokes: pr_create (POST /repos/objectstack-ai/objectstack/pulls + POST /issues/26281/assignees, run 40380541994, read-back byte-identical) and this os-dev-report comment (POST /issues/26200/comments). git push is not counted.", "open_questions": [], "out_of_scope_findings": [ "carrier: domain:server seat 1 · cloud-lane verification, no cloud code change known to be needed: the Console mount now reads the kernel's `storage` service at document time. If objectstack-ai/cloud objectos-runtime registers StorageServicePlugin on the same kernel it starts createConsoleStaticPlugin in, hosted uploads work once the framework pin passes PR 26281 and the OS_CONSOLE_CSP_CONNECT_SRC stop-gap can be removed. If it composes storage elsewhere (another kernel, or mountStorageRoutes over a service that kernel never sees), the CSP adds nothing on hosted and the call site must register the storage service on the Console's kernel. Not measurable from this repository. Acceptance step: on a hosted env with R2 and no OS_CONSOLE_CSP_CONNECT_SRC, the document's connect-src names the presigned upload origin and the presigned -> PUT -> complete -> download round trip passes. dedupe words: objectos-runtime storage service console CSP, connect-src R2 stop-gap removal", "noted, not filed (PR Acceptance notes): the accessor is not on the IStorageService contract (claim forbids packages/spec), so an out-of-repo adapter presigning to another host is not covered until it implements the method; LocalStorageAdapter's baseUrl option could make local presigned URLs cross-origin, but no producer in this repository sets it and the ruling says local adds nothing. carrier: none" ] }Generated by Claude Code
objectstack-fleet commentedon Oct 26, 2026
Landed
PR #26281 merged at 2026-10-26T19:57Z by the merge queue as
69aa630b9fonmain(Fixes #26200; ACCEPT6424921039).git ls-remote origin 'refs/heads/gh-readonly-queue/main/pr-26281-*'origin/mainby contentgit cat-file -e origin/main:packages/services/service-storage/src/s3-storage-adapter.tsgit merge-base --is-ancestor 69aa630b9f origin/mainState: this card closed by
Fixes(completed);pm:dispatchedremoved.Generated by Claude Code