Order Permissions¶
Owners of an order can invite other platform users as members and grant them a curated set of permissions controlling what they can do on that order. The order owner always has full access.
Admin access is not affected. Users with system permissions (ManageProvisioning, ViewAllOrders, etc.) and application tokens retain their existing blanket access.
Domain Design¶
OrderPermission enum¶
pub enum OrderPermission {
ViewOrder,
ViewTerminal,
ToggleServer,
BrowseFiles,
ManageExternalAccess,
ManageBillingAndResize,
}
ViewOrder is the baseline required to see the order at all. When granting any other permission the API auto-includes ViewOrder.
OrderMember entity¶
pub struct OrderMember {
id: OrderMemberId,
pub order_id: OrderId,
pub user_id: UserId,
pub permissions: Vec<OrderPermission>,
pub created_at: DateTime<Utc>,
}
OrderMemberRepository¶
async fn find_by_id(id) -> Option<OrderMember>
async fn find_by_order_id(order_id) -> Vec<OrderMember>
async fn find_by_order_and_user(order_id, user_id) -> Option<OrderMember>
async fn save(member) -> OrderMember
async fn set_permissions(member_id, permissions) // atomic replace
async fn delete(member_id)
Policy Changes¶
ReadOrderPolicy and ManageOrderPolicy are extended with one additional Allow branch for order members. The admin paths are checked first and are completely unaffected.
Updated ReadOrderPolicy¶
1. Application token → Allow
2. User with ViewAllOrders → Allow
3. user.id() == order.owner_id → Allow
4. member.has_permission(ViewOrder) → Allow (NEW)
5. Otherwise → Deny
Updated ManageOrderPolicy¶
1. Application token → Allow
2. User with ManageProvisioning → Allow
3. user.id() == order.owner_id → Allow
4. member has the required OrderPermission → Allow (NEW)
5. Otherwise → Deny
Per-Endpoint Member Permission Required¶
| Endpoint | Member permission |
|---|---|
GET /orders/{id} |
ViewOrder |
GET /orders/{id}/provisions |
ViewOrder |
GET /orders/{id}/status |
ViewOrder |
GET /orders/{id}/invoices |
ManageBillingAndResize |
GET /orders/{id}/terminal (WS) |
ViewTerminal |
POST /orders/{id}/toggle |
ToggleServer |
POST /orders/{id}/resize |
ManageBillingAndResize |
POST /orders/{id}/cancel |
ManageBillingAndResize |
GET /orders/{id}/external-access |
ViewOrder |
POST /orders/{id}/external-access |
ManageExternalAccess |
| File system endpoints | BrowseFiles |
| Configure / reprovision / member management | owner/admin only |
Database Schema¶
CREATE TABLE order_members (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
order_id UUID NOT NULL REFERENCES orders(id) ON DELETE CASCADE,
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
UNIQUE (order_id, user_id)
);
CREATE TABLE order_member_permissions (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
member_id UUID NOT NULL REFERENCES order_members(id) ON DELETE CASCADE,
permission TEXT NOT NULL,
granted_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
HTTP API¶
Routes mounted under /api/v1/orders.
GET /api/v1/orders/{id}/members¶
Lists all members with their permissions. Accessible to the order owner, members with ViewOrder, and admins.
[
{
"id": "<member_uuid>",
"user_id": "<user_uuid>",
"user_name": "alice",
"permissions": ["view_order", "view_terminal", "toggle_server"],
"created_at": "2026-03-12T10:00:00Z"
}
]
POST /api/v1/orders/{id}/members¶
Adds a member. Owner or admin only.
DELETE /api/v1/orders/{id}/members/{member_id}¶
Removes a member. Cascades to order_member_permissions via DB.
PUT /api/v1/orders/{id}/members/{member_id}/permissions¶
Replaces the full permission set for a member. ViewOrder is auto-included.
Dashboard UI¶
A "Access" tab in the order detail screen, visible only to the order owner.
Permission labels¶
| Enum variant | Display label |
|---|---|
view_order |
View |
view_terminal |
Terminal |
toggle_server |
Start / Stop |
browse_files |
File browser |
manage_external_access |
External ports |
manage_billing_and_resize |
Billing & resize |
View is always pre-checked and disabled when any other permission is selected.
Implementation Order¶
OrderPermissionenum +OrderMemberentity +OrderMemberRepositorytrait + migrationPgOrderMemberRepository- HTTP endpoints + user search endpoint (
GET /api/v1/users/search?q=) - Extend
ReadOrderPolicy+ManageOrderPolicywith member branch - Dashboard UI — Access tab