Filesystem-backed blob storage as the fifth concrete trigger kind.
- `files::collection(c).{create,head,get,update,delete,list}` Rhai SDK
(blob in/out; metadata maps; missing-field throws naming the field).
- `FilesService` trait in picloud-shared; `FsFilesRepo` (atomic
write: temp→fsync→rename→fsync-dir→DB; single-pass SHA-256;
checksum-verified reads → Corrupted) + `FilesServiceImpl` in
manager-core. Metadata in Postgres (0018), bytes on disk under
PICLOUD_FILES_ROOT with 0o700 shard dirs.
- `files:*` trigger kind via the Layout-E pattern (0019: widen both
CHECKs + files_trigger_details), TriggerEvent::Files (metadata only,
no bytes), emit_files fan-out, dispatcher arm, admin endpoint
POST /triggers/files (reuses validate_trigger_target).
- AppFilesRead/AppFilesWrite capabilities → script:read/script:write
(seven-scope commitment held). AppPubsubPublish reserved for v1.1.6.
- Admin files API (list + delete) + dashboard Files view per app.
Cross-app isolation keyed on cx.app_id at every layer. ~45 new tests
(service in-memory, fs tempdir, bridge integration). No DB required
for the suite. publish_ephemeral and the orphan sweep stay deferred.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
26 lines
1.2 KiB
SQL
26 lines
1.2 KiB
SQL
-- v1.1.5: filesystem-backed blob storage. The row holds metadata +
|
|
-- the SHA-256 checksum; the blob bytes live on disk at
|
|
-- <PICLOUD_FILES_ROOT>/files/<app_id>/<collection>/<id[0:2]>/<id>
|
|
-- (never in Postgres). Identity tuple is (app_id, collection, id) per
|
|
-- docs/sdk-shape.md, matching KV/docs collection scoping.
|
|
--
|
|
-- The checksum is computed in a single pass during the atomic write and
|
|
-- re-verified on read (FilesError::Corrupted on mismatch). Per-app
|
|
-- quotas are deferred to v1.2; only the per-file size cap is enforced
|
|
-- (in the service, not the schema).
|
|
CREATE TABLE files (
|
|
app_id UUID NOT NULL REFERENCES apps(id) ON DELETE CASCADE,
|
|
collection TEXT NOT NULL,
|
|
id UUID NOT NULL,
|
|
name TEXT NOT NULL,
|
|
content_type TEXT NOT NULL,
|
|
size_bytes BIGINT NOT NULL,
|
|
checksum_sha256 TEXT NOT NULL, -- hex, 64 chars, lowercase
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
PRIMARY KEY (app_id, collection, id)
|
|
);
|
|
|
|
-- List + cursor pagination scans by (app_id, collection).
|
|
CREATE INDEX idx_files_app_collection ON files (app_id, collection);
|