Skip to content

Workspace Surface Separation

Purpose

The product exposes distinct workspaces for distinct jobs. A feature should be placed in the narrowest surface that matches the user's intent and the required execution identity.

Surfaces

  • /portal is the end-user Storage Workspace. It is centered on storage spaces, simple file operations, shares, governance activity, usage, alerts, requests to storage admins, and user preferences.
  • /browser is the shared object explorer. Its Standard profile covers normal file work; its Advanced profile adds diagnostics, versions, metadata, tags, batch operations, and technical S3 inspection workflows.
  • /manager is the S3 and account configuration workspace. It owns native S3 and identity configuration, bucket properties, policies, users, groups, roles, lifecycle, replication, notifications, and topics.
  • /admin is the platform administration workspace. It owns UI users, endpoints, accounts, global quotas, billing administration, feature flags, audit, health, Portal request validation, and governance.

Browser Disablement Matrix

The Browser is shared across several surfaces, but feature disablement must not be treated as a replacement permission model. Native Browser contexts stay aligned with storage-side S3/IAM authorization. Portal Browser contexts first resolve visible Storage Spaces and roles from Portal database metadata and grants; IAM is only the synchronized projection for personal S3 keys and external storage enforcement. Browser gates should be used to reduce workflow exposure, prevent confusing execution identities, or keep a workspace focused on its job.

Current Workspace Profiles

Browser surface or profile Execution identity Current gate Already disabled or restricted today Decision notes
/browser standalone An active, unexpired private S3 connection owned by the current user with access_browser = true, or the user's personal Portal IAM identity for an explicitly enabled project. Private contexts use ownership, activity, expiry, and the Browser flag. Portal contexts additionally require portal_enabled, browser_portal_enabled, a compatible Portal role, and effective project setting portal.browser_access_enabled = true. All root contexts require browser_enabled and browser_root_enabled. RGW users, shared connections, generic account contexts, and forged catalogue IDs are rejected. Portal projects use the restricted Portal profile and only visible Storage Spaces. An invalid remembered context is cleared without fallback. Private connections use Standard or Advanced according to user/group capability. Portal projects always use the Portal functional profile and personal IAM credentials.
/manager/browser embedded Browser The active Manager ctx: Account root credentials, the selected RGW user's credentials, or an owned private connection's credentials. browser_enabled, browser_manager_enabled, manager_enabled, plus context-specific authorization. Account links require account_administrator and allow_manager_browser_data_access = true on the same row; RGW-user links aggregate the flag across direct and groups; private connections require access_manager = true and access_browser = true. Disabled by default. Existing association rows are not enabled by migration. Shared connections are always rejected, and every request must carry X-S3-Workspace: manager-browser. The Manager topbar and ctx remain authoritative. The UI displays the effective S3 identity and warns when RGW logs attribute operations to an admin/root or shared RGW identity instead of the UI user.
/portal/storage-spaces/:spaceId locked Browser Portal execution identity resolved for the selected account and DB-backed Storage Space. browser_enabled, browser_portal_enabled, portal_enabled, Portal account role, X-S3-Workspace: portal, active Storage Space visibility, DB grant role, and the explicit Portal profile. Enabled by default but locked to one active Storage Space. Bucket switching and technical S3 tools are hidden. The Portal passes already-resolved capability facts for upload, folder creation, deletion, restore, and sharing; Browser never reconstructs authorization from raw roles. Viewer capabilities remain read-only. Object details and deleted-object history open in the shared drawer without leaving the Storage Space page. This is the end-user file profile. Archived Storage Spaces remain blocked even if older credentials still have storage-side access. Backend Portal checks use DB metadata and grants; IAM policies are projection/enforcement for S3 keys, not the source for listings or roles.
/ceph-admin/browser embedded Browser Endpoint-wide Ceph Admin credentials for the selected Ceph endpoint. browser_enabled, browser_ceph_admin_enabled, ceph_admin_enabled, admin UI role, endpoint admin access, Ceph provider check, and an explicit risk acknowledgement dialog. Disabled by default. It uses embedded compact chrome and requires endpoint admin access. The UI warns that operations may execute with an owner identity different from the tenant owner. Keep disabled for regular object work. Prefer S3 Connections with the expected owner when tenant ownership matters.

Feature Families To Evaluate

Browser capability family Why it may be useful to disable by workspace or user Current implementation and gates Already disabled today Possible future decision point
Workspace availability Some deployments need Browser only in Portal or only for operators. Global browser_enabled plus per-surface flags: browser_root_enabled, browser_manager_enabled, browser_portal_enabled, browser_ceph_admin_enabled. Manager and Ceph Admin Browser are disabled by default; root Browser and Portal Browser are enabled by default. Decide whether defaults should differ by deployment profile, for example end-user-only, operator-only, or lab/demo.
Technical Browser tools Technical actions, advanced columns and search, versions, metadata mutation, batch operations, and bucket shortcuts can make a simple file workflow look like an operator console. Root checks effective browser_advanced_features_enabled, presented to administrators as Technical S3 tools. This functional profile does not control display preferences. Every root /browser user can choose Compact or Comfortable and independently show Folders. Root UI state v3 stores one set of root-only display preferences. Embedded surfaces receive their profile and density explicitly and never use root storage. Technical tools are unavailable to Standard and Portal profiles. Root Browser defaults to Compact with Folders hidden; Manager and Ceph Admin embeds are compact, while the locked Portal embed is comfortable. Compact combines the path and icon actions on one row when width permits; Comfortable shows labeled actions on that row only at extra-wide viewports and otherwise reserves a stable second row so selection does not shift the list. A row click opens its primary destination; checkboxes alone control selection. The action-bar More menu contains only secondary selection actions; path and Browser settings return when the selection is cleared. The unselected More menu avoids duplicating Upload and New folder, and separates current-path actions from technical tools. Object, path, and bucket details use one contextual drawer and never depend on selection. Advanced path details describe the virtual S3 prefix without loading misleading folder metadata; their recursive content count runs only on explicit request. The dedicated Browser exposes a read-only Bucket details summary; Manager and Ceph Admin embeds reuse the capability-filtered bucket editors. Add more granular admin toggles only if the technical bundle remains too broad.
Bucket switching and bucket creation Bucket selection and creation expose storage topology and can blur Portal Storage Space boundaries. Portal passes lockedBucketName; bucket creation requires full root Browser, non-embedded path, non-Portal profile, and advanced root access. Portal cannot switch buckets. Embedded surfaces and non-advanced root users do not get the Browser bucket creation shortcut. Consider a separate bucket-management toggle if root Browser should browse existing buckets but never create new ones.
Search options and object-list refinements Recursive, exact, case-sensitive, type, and storage-class search can be noisy or expensive for simple workspaces. Advanced exposes them; Standard and Portal keep the basic search experience. Advanced search is disabled in Standard and Portal. Consider per-user or per-workspace limits for recursive search on large buckets or cost-sensitive endpoints.
Basic file mutations Upload, folder creation, delete, and paste are expected for editors but not for read-only users. Standard and Advanced expose same-connection file mutations when state allows. Portal capability facts expose upload, folder creation, and delete only when already authorized. Unknown native S3 permissions are not guessed; a backend denial is reported explicitly. Paste/copy/cut are disabled in Portal. Portal Viewer capabilities hide mutations. Add similar UI hiding for native connections only if a reliable resolved capability signal exists.
Preview, properties, raw metadata, tags, and advanced object details Metadata, tags, storage class, headers, ACL-like details, legal hold, retention, and restore controls are operational details that may be inappropriate in end-user spaces. Primary activation always opens the common drawer on Preview. Standard adds a read-only Details tab. Advanced adds Versions, editable Properties, Access & Protection, and Archive when applicable. Portal composes Preview, History, Sharing, and Details through one shared Storage Space drawer in both the Portal page and a root Browser Portal context; only the Portal page persists drawer state in its URL. Technical detail tools are disabled in Standard and Portal. Portal sharing and restore actions remain filtered by resolved capabilities. Keep the common preview threshold and supported-type policy synchronized across surfaces.
Presigned URL and path sharing helpers Copying presigned URLs can increase data exfiltration risk or bypass preferred sharing workflows. Copy URL is an Advanced action and is also disabled when SSE-C mode is active because required encryption headers are missing. Disabled in Standard and Portal Browser. Disabled in SSE-C mode. Add an explicit per-user/per-workspace presign toggle when public-link or sharing policy must be centralized.
Copy, cut, paste, and cross-context moves Cross-context movement can cross tenant, account, or ownership boundaries. Standard supports copy/cut/paste only inside the current connection. Advanced also supports cross-context transfers; moves copy first and remove the source only after destination verification. Clipboard actions are disabled in Portal. Cross-context transfer is disabled in Standard. Preserve the connection boundary as part of the dispatcher input, not a view-local allowlist.
Versioning, deleted-object, restore, and cleanup tools Version listing, restore-to-date, deleted-object display, and cleanup are powerful and potentially destructive. Advanced version actions are visible only when bucket versioning is enabled or suspended. Portal allows read-only version status/listing for the mixed file view, plus capability-authorized single-file and folder-prefix restore flows. Technical restore-to-date and cleanup remain excluded from Portal. Portal shows deleted files only on explicit request and only in the current folder. Deleted rows cannot enter normal file selections. Viewer capabilities cannot restore. Cleanup and technical version tools remain disabled in Portal Browser. Keep listing on demand with S3 cursors; do not add a global trash index, periodic refresh, or implicit root-space restore.
Bulk operations Bulk delete, bulk attributes, and bulk restore amplify mistakes and can create large backend workloads. Advanced exposes bulk attributes and restore-to-date when selection state allows it. Portal keeps only capability-authorized end-user delete and restore actions. Technical bulk actions are disabled in Standard and Portal. Add batch-size, dry-run, or role gates for large selections, especially on shared connections.
Multipart upload supervision Listing and aborting orphaned multipart uploads is operational maintenance, not a normal file-user task. Full Browser can list multipart uploads and abort individual uploads. Portal backend allows active multipart upload lifecycle calls needed for uploads but not the multipart listing route. Multipart upload listing is unavailable in Portal Browser. Keep supervision in operator profiles unless Portal needs user-visible recovery for their own failed uploads.
SSE-C controls Customer-provided encryption keys create handling and support risks. SSE-C controls require endpoint capability and are disabled for the Portal basic profile. Disabled in Portal Browser and unavailable when endpoint capabilities do not advertise SSE support. Consider restricting SSE-C to advanced users only, or disabling it per endpoint when support teams cannot recover from user-managed keys.
Proxy transfers and transfer concurrency Backend proxy mode and high parallelism affect backend load and security posture. Browser settings expose global proxy mode, ZIP streaming threshold, and upload/download/operation parallelism. These are not per-user or per-workspace today. Not disabled per workspace or user today. Decide whether high-throughput or proxy features need stricter defaults for Portal, shared connections, or constrained deployments.

Portal Browser object operations are data-plane activity and are not persisted in application audit_logs. The Browser operation bar may show in-session progress, but it is not durable history. Object-level evidence comes from provider S3 access logs and the personal Portal IAM execution identity.

Manager Browser object operations follow the same audit boundary and are not persisted in application audit_logs. The manager-browser header is an authorization surface, not a catalogue or stored selection. Authorization is recomputed on every request so association or connection revocation takes effect immediately.

Portal identities appear in root /browser only when the effective project setting portal.browser_access_enabled is true. They still run with the personal IAM identity and remain limited to visible Storage Spaces. Internal bucket names remain execution identifiers and must not appear in Portal-facing labels.

Portal Rules

  • Keep Portal labels user-oriented: Storage Spaces, Shares, History, Storage health, Help requests, and Settings. History separates governance Activity from manager-only provider Access logs when available; the tab bar is hidden when Activity is the only view.
  • Do not add a /portal/browser route. Portal may embed the main Browser on /portal/storage-spaces/:spaceId, in a locked Storage Space context with the Portal functional profile, Standard layout, comfortable density, resolved capability facts, and X-S3-Workspace: portal. Root /browser may publish the same identity as a distinct portal_account context only when the effective project setting explicitly enables it.
  • Do not use Portal as a shortcut to Manager configuration.
  • Do not expose policy documents, principals, ARNs, advanced ACLs, object diagnostics, bucket defaults, lifecycle, CORS, replication, or versioning in Portal UI text.
  • Do not reintroduce a Storage Space Type field. Use visibility for private or shared, share_scope for restricted versus all-account sharing, and archived_at/status for archived state.
  • Private Storage Space creation is exposed through the dedicated can_create_private_storage_spaces Portal state flag. Team creation uses can_create_team_storage_spaces. Do not reuse can_manage_buckets for portal-user creation UI, because bucket management remains a broader portal-manager/operator capability. Both Portal roles need the private-creation setting to create a private space. Only a portal_manager can create or import a team space; named bucket mode still requires the named bucket creation setting.
  • Private Storage Spaces are visible only to their owner and Portal managers. Portal managers have full UI and content access and can explicitly take ownership. Visibility is immutable after creation. Team Storage Spaces have no owner and either use DB-backed Viewer and Editor grants from portal_storage_space_grants, or share_scope = account to grant the configured default role to current and future effective Portal members of the account. Portal must not silently create account membership when adding an individual share; users outside the account require a separate admin workflow. Archived Storage Spaces suspend Portal access and public links without deleting stored grants or links.
  • Portal user usage views may show global account usage and quota pressure, but named Storage Space breakdowns and traffic rows must be scoped to content-accessible Storage Spaces. Any hidden remainder must be represented only as an anonymous Other aggregate with no bucket or Storage Space identifiers.
  • Portal-managed bucket policies must only add, replace, or remove dedicated Sid statements such as PortalStorageSpaceAccess and PortalStorageSpaceArchived. They must preserve unrelated bucket policy statements. The Portal role templates for Viewer, Editor, Owner, and Manager are code-owned backend projections from DB grants; they are not an editable bucket/IAM policy document in Portal.
  • Because one RGW account maps to one Portal project, the code-owned portal-manager IAM group carries both the minimal global bootstrap actions (s3:ListAllMyBuckets, sts:GetSessionToken) and the explicit Manager data-plane action set on that account's bucket and object ARNs. Do not reintroduce iam:*, s3:*, or s3:CreateBucket. The only mutating bucket configuration actions in this group are the least-privilege s3:PutBucketVersioning and s3:PutLifecycleConfiguration permissions used by the Portal Storage Space settings workflow. Technical buckets must override this group access with a resource-policy Deny for manager IAM user principals.
  • Keep Portal authorization backed by Portal database metadata and grants. Storage IAM policies are synchronized projections for personal S3 keys and external enforcement; they must not be read back as the Portal source of listings or roles.
  • Keep Portal requests on /portal/requests as an end-user submission and follow-up surface. Portal users can request project membership for another person, remove a Portal user, or request a target quota change, but the approval workflow and execution stay in Admin on /admin/portal-requests.

Routing Contract

Portal canonical routes are:

  • /portal
  • /portal/profile
  • /portal/storage-spaces
  • /portal/storage-spaces/:spaceId
  • /portal/access-keys
  • /portal/shares
  • /portal/shares/:userId
  • /portal/history
  • /portal/usage
  • /portal/requests
  • /portal/settings

Object details stay inside /portal/storage-spaces/:spaceId. Deep links use the object query parameter for the full object key and object_view for preview, history, sharing, or details. prefix preserves the folder; object_deleted=1 identifies a deleted object, and show_deleted=1 includes deleted files in the listing. Closing the drawer keeps the Storage Space and folder context. Former /portal/storage-spaces/:spaceId/objects/* URLs are not routed or translated; consumers must use the canonical query parameters.

/portal/history is the only Portal history destination. Its default Activity view contains governance changes only. view=access selects manager-only S3 provider access logs. Former Portal transfer URLs are not routed.

Portal administration mock pages such as /portal/users, /portal/groups, and /portal/policies are intentionally not routed in the production Portal surface. They can return later only as real user-facing Portal features or isolated demo/test fixtures. /portal/access-keys is a real user-facing Portal feature for the current user's personal keys and Storage Space-scoped external credentials. It must never expose the active Portal runtime key. The Admin counterpart for storage-admin triage is /admin/portal-requests.

Portal Backend Cleanup Notes

Portal uses Storage Space, file, share, governance activity, access-log, usage, alert, billing-source, health, request, and delegated settings endpoints only. Legacy backend routes that exposed bucket-centric or advanced identity concepts have been removed from the Portal router and API client:

  • /portal/buckets*;
  • /portal/bootstrap;
  • /portal/users* bucket-grant and Portal user-management routes;
  • /portal/account-settings;
  • /portal/iam-compliance*.

Use /portal/storage-spaces* and /portal/storage-spaces/{spaceId}/shares* for end-user collaboration workflows. The shared project override is governed through /admin/accounts/{accountId}/portal-settings; its delegation flag is disabled by default. GET /portal/settings exposes the effective values to all project members. When delegation is enabled, PUT /portal/settings lets only the project's portal_manager update that same override; disabling delegation makes an existing override read-only without deleting or deactivating it.

GET /portal/storage-spaces/{spaceId}/settings exposes Versioning, the Portal Lifecycle baseline, and version history retention to Storage Space Owners and Portal Managers. Only a project portal_manager may call the corresponding PUT, using the actor's personal Portal IAM identity. The workflow owns only the ExpireDeleteMarkers and ExpireOldVersions lifecycle rules and must preserve every foreign lifecycle rule, including on imported buckets. Archived spaces remain read-only. Native IAM, policy compliance, access-key, and all other bucket administration workflows belong outside the Portal user surface. Personal Portal preferences belong to /portal/profile and use the simple /users/me ui_preferences contract. Stored preferences such as theme and default Portal account are UI defaults only; they never grant account access.

Breaking API Notes

Portal clients must not call the removed legacy endpoints listed above. Use these replacement surfaces instead:

  • GET /portal/accounts returns the Portal project catalog only: project ID, name, RGW account ID, required effective Portal role, and endpoint display and capability metadata. Admin account fields and the always-empty account quota placeholders are no longer part of this response; use /portal/usage for effective account quotas and consumption.

  • Storage Space list/detail/create/update: /portal/storage-spaces*.

  • Simple object list/detail/upload/download/delete/folders: /portal/storage-spaces/{spaceId} for the locked Browser file profile and query-driven object drawer. The backend object API remains under /api/portal/storage-spaces/{spaceId}/objects*; these API paths are not frontend navigation routes.
  • Collaboration: /portal/storage-spaces/{spaceId}/shares*.
  • Public links: /portal/storage-spaces/{spaceId}/public-links*.
  • Usage, governance activity, alerts, traffic, health, and billing source: the remaining Portal read endpoints.
  • Manager-only provider S3 access logs: /portal/access-logs/page and /portal/access-logs/raw. The removed non-paginated /portal/access-logs route and /portal/transfers/server-access-logs* aliases deliberately return 404.
  • External S3 credentials: /portal/access-keys, excluding the active Portal runtime key. Personal keys follow the user's Portal grants; external credentials are dedicated IAM users limited to one Storage Space and one selected permission level.
  • Portal storage-admin requests: /portal/requests* for the current user's submissions and /admin/portal-requests* for Admin review, messages, approval, and rejection.
  • Super-admin Portal override governance: /admin/accounts/{accountId}/portal-settings.
  • Project settings visibility and delegated shared override editing: GET/PUT /portal/settings.
  • Storage Space Versioning, Lifecycle, and version history retention: GET/PUT /portal/storage-spaces/{spaceId}/settings.

Native IAM access keys, IAM compliance remediation, bucket-user grants, and bucket-centric administration should be implemented in Manager or Admin when they are needed by operators.

Portal Data Flow

Portal uses thin FastAPI route handlers in backend/app/routers/portal.py and keeps business logic in PortalService. Portal request routes live in backend/app/routers/portal_requests.py and backend/app/routers/admin/portal_requests.py; all request business logic stays in PortalRequestsService. The frontend API clients are frontend/src/api/portal.ts and frontend/src/api/portalRequests.ts, and production Portal pages live under frontend/src/features/portal.

The current backend flow is:

  1. Resolve the authenticated UI user and Portal account binding.
  2. Resolve visible Storage Spaces from portal_storage_space_metadata, ownership, Portal manager status, visibility, share scope, archive state, effective account membership, and DB grants. Buckets without Portal metadata are not Storage Spaces.
  3. Map private owners to Owner, project managers to Manager, and team collaborators to Viewer or Editor. Portal managers have full content access to every project Storage Space.
  4. Block archived Storage Spaces from Portal object routes, embedded Browser bucket targets, sharing, and public-link downloads.
  5. Execute file and sharing operations with the Portal execution identity. The locked Browser embed must send X-S3-Workspace: portal so /browser routes resolve Portal credentials and enforce the minimal file profile.
  6. Apply platform-owned bucket defaults and synchronize code-owned IAM group, user, and bucket-policy projections from database state. The manager group grants account-wide Storage Space data access; technical buckets deny the individual manager principals with resource policies.
  7. Record only Portal control-plane, security, configuration, and global workflow-control actions through application audit logging. Object data-plane operations use provider S3 access logs.
  8. Return user-facing shapes without policy JSON, principals, ARNs, or advanced S3 diagnostics.

Portal request approvals are Admin-owned mutations. A Portal user creates a typed payload on /portal/requests, then an Admin approves, rejects, or sends messages from /admin/portal-requests. Approval must use processing to avoid double execution, audit the decision, notify the requester in-app, and either link/create the requested Portal UI user, remove a Portal user link, or apply the target account capacity quota through the existing account service. Portal must block quota targets below the currently used capacity when that usage is known.

Storage Space remains an API/UI abstraction. In v1 it maps to a bucket, but the UI must keep the Storage Space label so future project, dataset, or workspace concepts can be introduced without another surface rewrite.

Portal Test Group

Use focused Portal validation before broader suites:

  • Frontend Portal unit and route checks, from frontend/: rtk npm run test:portal
  • Frontend typecheck, from frontend/: rtk npm run typecheck
  • Frontend dead-code check, from frontend/: rtk npm run deadcode:check
  • Backend Portal service and route-contract checks: rtk env PYTHONPATH=backend backend/.venv/bin/pytest backend/tests/test_portal_service.py backend/tests/test_manager_workspace_access_rules.py -q
  • Backend Portal request workflow checks: rtk env PYTHONPATH=backend backend/.venv/bin/pytest backend/tests/test_portal_requests_service.py backend/tests/test_portal_requests_routes.py -q
  • Diff hygiene: rtk git diff --check

The Portal backend tests include permission regressions for Viewer, Editor, and Owner across object detail/download/delete, sharing, and removed advanced settings routes. Simple file listing, upload, and folder creation for Storage Spaces are covered through the locked Browser profile.

Portal Fallback Policy

Production Portal pages use real Portal APIs first. When a backend capability is absent, the UI must show an empty or unavailable state instead of generated production-looking data.

Allowed deterministic fixture data is limited to tests, docs screenshots, and isolated demo setup. It must not be imported by production Portal pages.

Portal Visual QA

Portal has a deterministic local QA scenario in the docs screenshot Playwright setup:

  • authenticated user: storage.user@example.com;
  • selected Portal account: Helios Retail (selectedPortalAccountId=101);
  • fixture Storage Spaces: genomics-2026, photos, and datasets;
  • fixture shares, activity, alerts, traffic, usage, billing source, and locked Browser object listing data;
  • no live storage credentials or backend state are required.

The reference desktop screenshots are generated by npm run docs:screenshots from frontend/ and are committed under doc/docs/assets/screenshots/user/ for:

  • workspace-portal;
  • portal-storage-spaces;
  • portal-object-list;
  • portal-object-detail;
  • portal-usage;
  • portal-access-keys;
  • portal-settings.

portalVisualQa.spec.ts also opens these Portal routes in desktop and mobile viewports:

  • /portal
  • /portal/storage-spaces
  • /portal/storage-spaces/genomics-2026?prefix=raw-data%2F2024%2F03%2F
  • /portal/storage-spaces/genomics-2026?tab=settings
  • /portal/storage-spaces/genomics-2026?prefix=raw-data%2F2024%2F03%2F&object=raw-data%2F2024%2F03%2Fsample_001.fastq.gz&object_view=details
  • /portal/shares
  • /portal/history
  • /portal/history?view=access
  • /portal/usage
  • /portal/requests
  • /portal/access-keys
  • /portal/settings

The QA test checks that the main content renders, the page does not expose /portal/browser, the locked Storage Space file browser renders without advanced Browser entry points, the document does not create viewport horizontal overflow, and keyboard focus can leave the body on the first tab. Mobile screenshots are not committed; mobile coverage is kept as a lightweight automated viewport check to avoid expanding the documentation image set.