feat(v1.1.7-secrets): secrets SDK + table + admin API + dashboard

Encrypted per-app secrets, reachable from scripts as
secrets::{get,set,delete,list}(name) and managed from the dashboard
Secrets tab. Values are AES-256-GCM-sealed with the process master key
(picloud_shared::crypto) before they touch Postgres; the repo only ever
sees ciphertext + nonce. JSON round-trip preserves Rhai types.

- migration 0023_secrets.sql (PRIMARY KEY (app_id, name)).
- SecretsService trait (picloud-shared) + SecretsServiceImpl + repo
  (manager-core), wired into the Services bundle and Rhai engine.
- Capability::AppSecretsRead/Write (→ script:read / script:write); no
  new Scope variants (seven-scope commitment).
- Admin API GET/POST/DELETE /apps/{id}/secrets (list returns names +
  updated_at, never values).
- build_app now takes a MasterKey, sourced from PICLOUD_SECRET_KEY in
  main.rs; test callers pass a fixed test key.
- 64 KB value cap (PICLOUD_SECRET_MAX_VALUE_BYTES); no ServiceEvent
  emission (secret writes don't fire triggers, by design).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
MechaCat02
2026-06-04 21:37:17 +02:00
parent dc2e4fa01f
commit 2d11090d1a
28 changed files with 1959 additions and 35 deletions

View File

@@ -22,7 +22,8 @@ use std::sync::Arc;
use crate::{
DeadLetterService, DocsService, FilesService, HttpService, KvService, ModuleSource,
NoopDeadLetterService, NoopDocsService, NoopEventEmitter, NoopFilesService, NoopHttpService,
NoopKvService, NoopModuleSource, NoopPubsubService, PubsubService, ServiceEventEmitter,
NoopKvService, NoopModuleSource, NoopPubsubService, NoopSecretsService, PubsubService,
SecretsService, ServiceEventEmitter,
};
/// SDK service bundle. See module docs for the lifecycle and the v1.1.x
@@ -73,6 +74,12 @@ pub struct Services {
/// publish-time outbox fan-out in the picloud binary;
/// `NoopPubsubService` in tests that don't publish.
pub pubsub: Arc<dyn PubsubService>,
/// Encrypted per-app secrets (v1.1.7). Scripts get
/// `secrets::{get,set,delete,list}(name)`. Backed by an
/// AES-256-GCM-at-rest Postgres repo in the picloud binary;
/// `NoopSecretsService` in tests that don't touch secrets.
pub secrets: Arc<dyn SecretsService>,
}
impl Services {
@@ -90,6 +97,7 @@ impl Services {
http: Arc<dyn HttpService>,
files: Arc<dyn FilesService>,
pubsub: Arc<dyn PubsubService>,
secrets: Arc<dyn SecretsService>,
) -> Self {
Self {
kv,
@@ -100,6 +108,7 @@ impl Services {
http,
files,
pubsub,
secrets,
}
}
@@ -119,6 +128,7 @@ impl Services {
Arc::new(NoopHttpService),
Arc::new(NoopFilesService),
Arc::new(NoopPubsubService),
Arc::new(NoopSecretsService),
)
}
}