Storage profiles
The Storage profiles screen lets you declare the object storage of the client (S3, Azure Blob, GCS, IBM COS,
SeaweedFS or S3 compatible), verify its capabilities by real calls, list and delete objects, and issue presigned URLs.
Portal path: /data/storage. Tenant administrators manage the tenant profiles, platform administrators the platform
profiles; security officers verify them; developers and agents use presigned URLs.
What the screen shows
A notice at the top recalls that a profile never holds a secret: credential_ref points to OpenBao or to an
environment variable, and WORM needs object lock and retention verified. Three tabs:
- Profiles: the table with Name, Scope, Provider, Bucket, Purposes, Status, WORM,
Missing capabilities, Verified at. WORM is computed by the portal:
not_wormwithout theaudit_wormpurpose,qualifiedwhenobject_lockandretentionare verified,missing_capabilityotherwise. Filters: Scope (tenant,platform), Provider and Purpose (audit_worm,evidence,documents,media,exports,backups,general). A click on a row opens the profile. - Objects of a profile: a Profile id field with Apply, then the Objects table with Key, Size
(bytes), Type, Storage class, Object version, ETag, Last modified, and a Prefix filter
(example
docs/). - Presigned URL: a form with Profile (required), Scope (
readorwrite, required), Key (required), Expiry (seconds) (900) and Type, and its result panel.
The New profile form has Name (required, lowercase, digits and -), Provider (required), Bucket
(required), Scope, Endpoint, Region, Key prefix, Authentication (JSON, default
{"method": "sigv4"}), Credential reference (example openbao://storage/s3), Purposes and Description.
Actions
| Button or action | What it does | API call | Roles |
|---|---|---|---|
| Refresh icon, Filter | Reloads the profiles | GET /v1/storage/profiles | tenant_admin, security_officer, developer, auditor, platform_admin |
| New profile > Create | Creates the profile (no secret accepted) | POST /v1/storage/profiles | tenant_admin; platform_admin for the platform scope |
| Click on a row | Opens the profile | GET /v1/storage/profiles/{profile_id} | same as the list |
| Verify signature (row menu) | Opens a dialog with Probe write (unchecked), then tests the capabilities by real calls | POST /v1/storage/profiles/{profile_id}/verify | tenant_admin, security_officer; platform_admin for the platform scope |
| Checks (row menu) | Shows the history of the capability checks | GET /v1/storage/profiles/{profile_id}/checks | same as the list |
| Disable (row menu, active profile) | Asks a confirmation, then sets the status disabled | PATCH /v1/storage/profiles/{profile_id} | tenant_admin; platform_admin for the platform scope |
| Enable (row menu, disabled profile) | Sets the status active | PATCH /v1/storage/profiles/{profile_id} | same as Disable |
| Delete (row menu) | Shows the impact confirmation, then deletes the profile | DELETE /v1/storage/profiles/{profile_id} | same as Disable |
| Apply (Objects) | Lists the objects under the presign prefixes | GET /v1/storage/profiles/{profile_id}/objects | tenant_admin, developer, agent, auditor |
| Delete (object menu) | Asks a confirmation, then deletes the object (and its version when known) | POST /v1/storage/profiles/{profile_id}/objects/delete | server: tenant_admin, developer, agent; shown to tenant_admin, platform_admin |
| Run (Presigned URL) | Issues a read (GET) or write (PUT) URL, shown once | POST /v1/storage/profiles/{profile_id}/presign | tenant_admin, developer, agent; platform_admin for the platform scope |
Roles
The portal hides the buttons with the lists of portal/src/lib/kit.ts: ROLES.storageManagers (tenant_admin,
platform_admin) for New profile, Disable, Enable, Delete and the object deletion,
ROLES.storageVerifiers (tenant_admin, security_officer, platform_admin) for Verify signature, and
ROLES.storagePresign (tenant_admin, developer, agent, platform_admin) for the presign form; other users see
"Requires one of these roles". The Control Plane decides in
services/control-plane/src/eai/domains/storage/routes.py; every action also goes through the policy decision and is
audited.
Typical tasks
Declare and verify a profile
-
Create the profile. The profile only names where the secret lives. Click New profile, fill Name, Provider, Bucket, Endpoint if needed and Credential reference, then Create.
Expected result: the toast "Created" and the row in Profiles.
If it fails:
storage_secret_in_profilemeans a secret was typed in Authentication; move it to OpenBao. -
Verify it. Capabilities are discovered by real calls, never declared. Open the row menu, choose Verify signature, check Probe write to test writing too, then confirm.
Expected result: a side panel with
status,capabilitiesandmissing_capabilities; Verified at is filled.If it fails: the
errorblock of the answer gives the provider error;storage_credential_invalidpoints to the credential reference.
Give an application a download link
-
Issue the URL. A read URL cannot write and the reverse. Open Presigned URL, fill Profile, Scope
read, Key, then Run.Expected result: the answer holds
url,methodGETandexpires_at. Copy the URL now: it is shown only once.If it fails:
storage_key_out_of_scopemeans the key is outside the presign prefixes of the profile.
Clean an object
-
List the objects. Open Objects of a profile, paste the profile id, then Apply.
Expected result: the Objects table.
If it fails:
storage_prefix_requiredmeans the profile has several presign prefixes; fill Prefix and click Filter. -
Delete it. Open the object menu, choose Delete and confirm.
Expected result: the toast "Done".
If it fails:
storage_object_protectedmeans the profile holds WORM audit exports or evidence; retention applies.
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
forbidden (403) with required_roles | Your role is not allowed for this action | See the Roles section |
storage_scope_forbidden (403) | A platform profile is managed by platform_admin only | Sign in as platform administrator |
storage_profile_exists (409) | The name is already used | Choose another name |
storage_profile_disabled (409) | The profile is disabled | Enable it first |
storage_secret_in_profile (422) | A secret field was sent in the profile | Store the secret in OpenBao and use Credential reference |
storage_credential_ref_invalid (422) | The reference is not openbao:// or env:// | Fix Credential reference |
storage_endpoint_required (422) | The provider needs an endpoint | Fill Endpoint |
storage_presign_expiry_invalid (422) | Expiry (seconds) is above the allowed maximum | Lower the expiry |
storage_key_out_of_scope (403) | Key outside the presign prefixes | Use a key under an allowed prefix |
storage_object_protected (409) | WORM audit or evidence profile | The object is never deleted through the API |