Skip to content

CLI: per-workspace rclone remotes for Team push/pull (tenant-scoped keys) #919

Description

@phernandez

Summary

Make bm cloud push/pull (and the rclone remote model generally) work for projects in non-default / Team workspaces by giving each accessible tenant its own rclone remote, provisioned with that tenant's bucket-scoped credentials. Follow-up to the Codex P1 review note on #917; complements the Team-safe transfer primitives from #858.

Background / findings (Tigris + cloud backend research)

  • Keys are tenant-scoped, not user-scoped. Cloud stores mount creds in tenant_mount_credentials keyed on tenant_id (no user_profile association). Decision: keep keys tenant-scoped (option A) — it matches Tigris's model and preserves per-tenant revocation.
  • Tigris access keys are bucket-scoped (confirmed via Tigris IAM docs + Fly Tigris docs). In BM, one tenant = one Tigris bucket (tenant.tigris_bucket_name).
  • Team = its own tenant = its own bucket. An organization maps 1:1 to a dedicated tenant; ACTIVE OrganizationMembers share it; subscription is org/tenant-level.
  • Mount endpoints already honor X-Workspace-ID. GET /tenant/mount/info and POST /tenant/mount/credentials resolve the workspace from the header (falling back to the user's default tenant), validate membership + subscription, and return bucket-scoped credentials for that tenant.
  • Caveat — legacy tenants leak keys: for non-partner-managed tenants, POST /tenant/mount/credentials mints a new IAM key every call and never revokes the old one. So the CLI must provision once per workspace and reuse, never per-operation. (Partner-managed tenants return stable stored creds.)

The problem

The CLI uses a single rclone remote basic-memory-cloud configured (at bm cloud setup) with the default tenant's bucket-scoped keys. Because keys/buckets are tenant-scoped, that one remote can only reach one bucket — so push/pull against a project in another (Team) workspace would target the wrong bucket and/or fail auth. get_mount_info() is also called without X-Workspace-ID, so it returns the default tenant's bucket regardless of the --name project's workspace.

Proposed design (option A: per-workspace remotes)

  1. Resolve project → workspace in push/pull: optimistically resolve the named project's workspace. If the name exists in multiple accessible workspaces, abort and list each candidate with tenant_id/slug to disambiguate (plus a --workspace override).
  2. One rclone remote per tenant (e.g. keep basic-memory-cloud for the default tenant; add basic-memory-cloud-<slug> for others), configured with that tenant's mount credentials obtained via /tenant/mount/credentials + X-Workspace-ID.
  3. Scope mount info: pass X-Workspace-ID: <resolved tenant_id> to get_mount_info() so the bucket is the project's tenant's bucket.
  4. Provision once, reuse (see legacy-tenant key-leak caveat). Membership/subscription are enforced server-side, so a team tenant mounts when the user is an active member with an active subscription.

Open question (UX) — see PR discussion

How a per-workspace remote gets provisioned: explicit bm cloud setup --workspace <ws> vs auto-provision-on-first-use vs hybrid.

Notes

  • Minor backend hygiene: tenant_mount_credentials.tenant_id has no FK constraint to tenant(id), unlike every sibling tenant-owned table — likely an oversight; add FOREIGN KEY (tenant_id) REFERENCES tenant(id) in basic-memory-cloud.

Related: #858, #917, #862.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    cloudBasic Memory CloudenhancementNew feature or request

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions