Storage configuration probes
StudioBrain validates a storage configuration before it encrypts and persists the new settings. The shared OpenDAL probe is intentionally destructive only to a randomly named sentinel object: it writes known bytes, reads them back, verifies the content, and deletes the sentinel. Local-filesystem setup also performs the preflight described below.
The implementation lives in crates/sb-server/src/routes/settings.rs in build_probe_operator, probe_operator, and update_storage_config.
Validation sequence
PUT /api/settings/storage performs these steps in order:
- Validate the provider-specific configuration and construct an OpenDAL operator.
- Write a random probe object containing known bytes.
- Read the same object and compare the returned bytes.
- Delete the probe object.
- Only after the probe succeeds, encrypt credentials and persist the configuration.
Any probe failure returns HTTP 400 with the provider and the concrete error. A failed probe does not replace the previously stored configuration.
The content comparison matters: a successful write call alone does not prove the configured endpoint, bucket, credentials, and read path refer to the same usable store.
Provider behavior
| Provider | Configuration validation before the sentinel probe |
|---|---|
| Local filesystem | The path must exist, be a directory, and accept a temporary-file write and removal. The current preflight uses the fixed reserved name .sb-config-probe.tmp; do not store user content under that name. StudioBrain then creates an OpenDAL filesystem operator rooted at the configured path. |
| S3-compatible | Requires a bucket; applies the configured region, endpoint, access key, and secret key to the OpenDAL S3 operator. The generic sentinel write/read/delete sequence is the authoritative connectivity test. |
| Azure Blob | Builds an OpenDAL Azure Blob operator from the configured container and credentials, then runs the same sentinel sequence. |
| Google Drive / Dropbox | Rejected by the core route because their OAuth-backed adapters belong to the cloud overlay. |
The core route does not issue a separate S3 HEAD Object request. Provider-specific HTTP operations are OpenDAL implementation details; the application-level contract is the write/read/content-check/delete sequence above.
Cleanup and failure semantics
The sentinel key contains a random UUID, so concurrent probes do not share an object name.
- If the read succeeds but returns different bytes, StudioBrain attempts a best-effort delete before returning the mismatch error.
- If the final delete fails, the probe fails and the new configuration is not persisted.
- If the read itself fails after a successful write, the route returns the error without the generated UUID. If provider-side inspection is required, look for stale
.sb-probe-*.tmpobjects and confirm their age before removal.
Do not weaken a delete failure into success. Leaving undeletable objects proves the credentials are not sufficient for normal StudioBrain storage lifecycle operations.
Capacity reporting is separate
Filesystem capacity is reported by GET /api/storage/info, which uses statvfs for local storage. Capacity or free-space reporting is not part of the configuration probe and does not establish read/write/delete correctness for a remote provider.
Operator verification
When troubleshooting a rejected configuration:
- Confirm the request reached
PUT /api/settings/storageand record the returned provider/error fields. - Verify the configured local directory, bucket, or container exists and that the supplied identity has create, read, and delete permissions.
- For S3-compatible services, verify the endpoint, region, and bucket are mutually consistent; do not infer success from an unrelated bucket-list operation.
- Check provider logs for the random sentinel key and remove an orphan only after confirming no probe is still active.
- Retry the settings update and verify that the response succeeds before assuming the encrypted configuration was persisted.
Tests for this route should cover a successful round trip plus write, read, content-mismatch, and delete failures. Provider builder tests should remain separate from the generic probe tests so the shared lifecycle contract cannot drift between backends.