Skip to content

WIP: Add service accounts#205

Draft
rakshith-ravi wants to merge 4 commits intodevelopfrom
feature/service-accounts
Draft

WIP: Add service accounts#205
rakshith-ravi wants to merge 4 commits intodevelopfrom
feature/service-accounts

Conversation

@rakshith-ravi
Copy link
Copy Markdown
Contributor

service accounts - non-human identity for runners/automation. workspace-scoped, single token, role-based RBAC.

RequestUserData refactored with IdentityData enum. CRUD endpoints, permission resolution, migration, integration tests.

remaining work:

  • add service accounts to ClientType, change route declarations to accept ClientType[]
  • runners should work from a service account
  • runner should send version number on register, UI should show if outdated
  • add ClientType::ApiToken test server, implement SA token auth tests (see api/tests/TODOs.md)

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds first-class support for service accounts (non-human identities) and introduces a client-type–aware access model across the Rust API/models, runners, and frontend, plus runner version handshakes for “outdated runner” UI signaling.

Changes:

  • Replace api = false/API_ALLOWED gating with client_type = [...] / ALLOWED_CLIENT_TYPES across endpoint macros and route mounting.
  • Introduce service accounts (RBAC resource + permissions, CRUD endpoints, DB tables/migrations, and permission resolution).
  • Add runner WebSocket handshake (exposure type + runner semver), persist runner version, and surface “outdated” status in the UI (with an API /version endpoint).

Reviewed changes

Copilot reviewed 214 out of 240 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
runners/common/src/utils/router_ext.rs Switch runner-side route mounting gate to ALLOWED_CLIENT_TYPES + ClientType.
runners/common/src/runner/database_sync.rs Send runner handshake (version + exposure type); handle handshake-required retries.
runners/common/src/resource_executor/mod.rs Adds ServiceAccount to resource-type match arm (still todo!()).
models/src/utils/mod.rs Export new client_type utility module.
models/src/utils/client_type.rs New ClientType enum shared across layers.
models/src/user_data.rs Refactor request identity into IdentityData; add client_type on RequestUserData.
models/src/rbac/resource_type.rs Add ResourceType::ServiceAccount.
models/src/rbac/permissions.rs Add ServiceAccountPermission and Permission::ServiceAccount(...).
models/src/lib.rs Re-export ServiceAccountPermission in prelude.
models/src/api/workspace/volume/update_volume.rs Add client_type list to endpoint.
models/src/api/workspace/volume/list_volumes.rs Add client_type list to endpoint.
models/src/api/workspace/volume/get_volume_info.rs Add client_type list to endpoint.
models/src/api/workspace/volume/delete_volume.rs Add client_type list to endpoint.
models/src/api/workspace/volume/create_volume.rs Add client_type list to endpoint.
models/src/api/workspace/update_workspace_info.rs Add client_type list to endpoint.
models/src/api/workspace/service_account/create_service_account.rs New service-account create endpoint model.
models/src/api/workspace/service_account/delete_service_account.rs New service-account delete endpoint model.
models/src/api/workspace/service_account/get_service_account_info.rs New service-account get endpoint model.
models/src/api/workspace/service_account/list_service_accounts.rs New service-account list endpoint model.
models/src/api/workspace/service_account/regenerate_service_account_token.rs New token-regeneration endpoint model.
models/src/api/workspace/service_account/update_service_account.rs New update endpoint model.
models/src/api/workspace/service_account/mod.rs New module exporting service-account models/endpoints.
models/src/api/workspace/runner/stream_runner_logs.rs Add client_type list to stream endpoint.
models/src/api/workspace/runner/stream_runner_data_for_workspace.rs Enforce client_type = [ServiceAccount]; add handshake messages & runner version.
models/src/api/workspace/runner/remove_runner_from_workspace.rs Add client_type list to endpoint.
models/src/api/workspace/runner/mod.rs Add version: Version field to Runner model.
models/src/api/workspace/runner/list_runners_for_workspace.rs Add client_type list to endpoint.
models/src/api/workspace/runner/get_runner_metrics.rs Add client_type list to endpoint.
models/src/api/workspace/runner/get_runner_logs.rs Add client_type list to endpoint.
models/src/api/workspace/runner/get_runner_info.rs Add client_type list to endpoint.
models/src/api/workspace/runner/get_ingress_token_for_runner.rs Add client_type list to endpoint.
models/src/api/workspace/runner/add_runner_to_workspace.rs Add client_type list to endpoint.
models/src/api/workspace/rbac/user/update_user_roles_in_workspace.rs Add client_type list to endpoint.
models/src/api/workspace/rbac/user/remove_user_from_workspace.rs Add client_type list to endpoint.
models/src/api/workspace/rbac/user/list_users_in_workspace.rs Add client_type list to endpoint.
models/src/api/workspace/rbac/role/update_role.rs Add client_type list to endpoint.
models/src/api/workspace/rbac/role/list_users_for_role.rs Add client_type list to endpoint.
models/src/api/workspace/rbac/role/list_all_roles.rs Add client_type list to endpoint.
models/src/api/workspace/rbac/role/get_role_info.rs Add client_type list to endpoint.
models/src/api/workspace/rbac/role/delete_role.rs Add client_type list to endpoint.
models/src/api/workspace/rbac/role/create_new_role.rs Add client_type list to endpoint.
models/src/api/workspace/rbac/list_all_resource_types.rs Add client_type list to endpoint.
models/src/api/workspace/rbac/list_all_permissions.rs Add client_type list to endpoint.
models/src/api/workspace/rbac/get_current_permissions.rs Add client_type list to endpoint.
models/src/api/workspace/mod.rs Export new service_account workspace API module.
models/src/api/workspace/managed_url/verify_configuration.rs Add client_type list to endpoint.
models/src/api/workspace/managed_url/update_managed_url.rs Add client_type list to endpoint.
models/src/api/workspace/managed_url/list_managed_url.rs Add client_type list to endpoint.
models/src/api/workspace/managed_url/delete_managed_url.rs Add client_type list to endpoint.
models/src/api/workspace/managed_url/create_managed_url.rs Add client_type list to endpoint.
models/src/api/workspace/is_name_available.rs Add client_type list to endpoint.
models/src/api/workspace/get_workspace_info.rs Add client_type list to endpoint.
models/src/api/workspace/domain/verify_domain_in_workspace.rs Add client_type list to endpoint.
models/src/api/workspace/domain/update_domain_dns_record.rs Add client_type list to endpoint.
models/src/api/workspace/domain/list_domains_in_workspace.rs Add client_type list to endpoint.
models/src/api/workspace/domain/is_domain_valid.rs Add client_type list to endpoint.
models/src/api/workspace/domain/get_domain_info_in_workspace.rs Add client_type list to endpoint.
models/src/api/workspace/domain/get_domain_dns_record.rs Add client_type list to endpoint.
models/src/api/workspace/domain/delete_domain_in_workspace.rs Add client_type list to endpoint.
models/src/api/workspace/domain/delete_dns_record.rs Add client_type list to endpoint.
models/src/api/workspace/domain/add_domain_to_workspace.rs Add client_type list to endpoint.
models/src/api/workspace/domain/add_dns_record.rs Add client_type list to endpoint.
models/src/api/workspace/deployment/update_deployment.rs Add client_type list to endpoint.
models/src/api/workspace/deployment/stream_deployment_logs.rs Add client_type list to stream endpoint.
models/src/api/workspace/deployment/stop_deployment.rs Add client_type list to endpoint.
models/src/api/workspace/deployment/start_deployment.rs Add client_type list to endpoint.
models/src/api/workspace/deployment/list_deployment.rs Add client_type list to endpoint.
models/src/api/workspace/deployment/list_all_deployment_machine_type.rs Add client_type list to endpoint.
models/src/api/workspace/deployment/get_deployment_metric.rs Add client_type list to endpoint.
models/src/api/workspace/deployment/get_deployment_logs.rs Add client_type list to endpoint.
models/src/api/workspace/deployment/get_deployment_info.rs Add client_type list to endpoint.
models/src/api/workspace/deployment/deploy_history/revert_deployment.rs Add client_type list to endpoint.
models/src/api/workspace/deployment/deploy_history/list_deploy_history.rs Add client_type list to endpoint.
models/src/api/workspace/deployment/deploy_history/delete_deploy_history.rs Add client_type list to endpoint.
models/src/api/workspace/deployment/delete_deployment.rs Add client_type list to endpoint.
models/src/api/workspace/deployment/create_deployment.rs Add client_type list to endpoint.
models/src/api/workspace/delete_workspace.rs Add client_type list to endpoint.
models/src/api/workspace/create_workspace.rs Add client_type list to endpoint.
models/src/api/workspace/container_registry/list_repository_tags.rs Add client_type list to endpoint.
models/src/api/workspace/container_registry/list_repository_manifests.rs Add client_type list to endpoint.
models/src/api/workspace/container_registry/list_repositories.rs Add client_type list to endpoint.
models/src/api/workspace/container_registry/get_repository_manifest_details.rs Add client_type list to endpoint.
models/src/api/workspace/container_registry/get_repository_info.rs Add client_type list to endpoint.
models/src/api/workspace/container_registry/get_exposed_ports.rs Add client_type list to endpoint.
models/src/api/workspace/container_registry/delete_repository_manifest.rs Add client_type list to endpoint.
models/src/api/workspace/container_registry/delete_repository.rs Add client_type list to endpoint.
models/src/api/workspace/container_registry/create_repository.rs Add client_type list to endpoint.
models/src/api/user/web_logins/list_web_logins.rs Replace api = false with client_type = [WebDashboard].
models/src/api/user/web_logins/get_web_login_info.rs Replace api = false with client_type = [WebDashboard].
models/src/api/user/web_logins/delete_web_login.rs Replace api = false with client_type = [WebDashboard].
models/src/api/user/update_user_info.rs Replace api = false with client_type = [WebDashboard].
models/src/api/user/search_for_user.rs Replace api = false with client_type = [WebDashboard].
models/src/api/user/recovery_options/verify_user_phone_number.rs Replace api = false with client_type = [WebDashboard].
models/src/api/user/recovery_options/verify_user_email.rs Replace api = false with client_type = [WebDashboard].
models/src/api/user/recovery_options/update_user_phone_number.rs Replace api = false with client_type = [WebDashboard].
models/src/api/user/recovery_options/update_user_email.rs Replace api = false with client_type = [WebDashboard].
models/src/api/user/mfa/get_mfa_secret.rs Add client_type = [WebDashboard].
models/src/api/user/mfa/deactivate_mfa.rs Add client_type = [WebDashboard].
models/src/api/user/mfa/activate_mfa.rs Add client_type = [WebDashboard].
models/src/api/user/list_user_workspaces.rs Add client_type list to endpoint.
models/src/api/user/get_user_info.rs Add client_type list to endpoint.
models/src/api/user/get_user_details.rs Add client_type list to endpoint.
models/src/api/user/change_password.rs Replace api = false with client_type = [WebDashboard].
models/src/api/user/api_token/update_api_token.rs Replace api = false with client_type = [WebDashboard].
models/src/api/user/api_token/revoke_api_token.rs Replace api = false with client_type = [WebDashboard].
models/src/api/user/api_token/regenerate_api_token.rs Replace api = false with client_type = [WebDashboard].
models/src/api/user/api_token/list_api_tokens.rs Replace api = false with client_type = [WebDashboard].
models/src/api/user/api_token/get_api_token_info.rs Replace api = false with client_type = [WebDashboard].
models/src/api/user/api_token/create_api_token.rs Replace api = false with client_type = [WebDashboard].
models/src/api/get_version.rs Add typed semver response and client-type gating.
models/src/api/auth/reset_password.rs Replace api = false with client_type = [WebDashboard].
models/src/api/auth/resend_otp.rs Replace api = false with client_type = [WebDashboard].
models/src/api/auth/renew_access_token.rs Replace api = false with client_type = [WebDashboard].
models/src/api/auth/oauth/token.rs Add client_type = [WebDashboard].
models/src/api/auth/oauth/revoke.rs Add client_type = [WebDashboard].
models/src/api/auth/oauth/introspect.rs Add client_type = [WebDashboard].
models/src/api/auth/oauth/authorize.rs Add client_type = [WebDashboard].
models/src/api/auth/logout.rs Replace api = false with client_type = [WebDashboard].
models/src/api/auth/login.rs Replace api = false with client_type = [WebDashboard].
models/src/api/auth/list_recovery_options.rs Replace api = false with client_type = [WebDashboard].
models/src/api/auth/is_username_valid.rs Replace api = false with client_type = [WebDashboard].
models/src/api/auth/is_email_valid.rs Replace api = false with client_type = [WebDashboard].
models/src/api/auth/forgot_password.rs Replace api = false with client_type = [WebDashboard].
models/src/api/auth/docker_login.rs Add client_type = [ApiToken].
models/src/api/auth/create_account.rs Replace api = false with client_type = [WebDashboard].
models/src/api/auth/complete_sign_up.rs Replace api = false with client_type = [WebDashboard].
models/src/api/api_endpoint.rs Replace API_ALLOWED with ALLOWED_CLIENT_TYPES.
models/Cargo.toml Add semver dependency with serde support.
macros/src/declare_stream_endpoint.rs Require client_type in stream endpoint declarations; emit ALLOWED_CLIENT_TYPES.
macros/src/declare_api_endpoint.rs Require client_type in API endpoint declarations; emit ALLOWED_CLIENT_TYPES.
frontend/src/routes/_logged-in/_workspaced/workspace/-components/workspace-header.tsx UI tweak: role create link styling change.
frontend/src/routes/_logged-in/_workspaced/runners/index.tsx Show runner version + outdated indicator; fetch API version.
frontend/src/routes/_logged-in/_workspaced/runners/-components/metrics.tsx Add runner identity/version panel and outdated hint in metrics view.
frontend/src/routes/_logged-in/_workspaced/runners/$id.tsx Default tab change; add delete modal; pass version info into metrics; fetch API version.
frontend/src/hooks/fetch/version.tsx New query hook for API version endpoint.
frontend/src/hooks/fetch/index.tsx Export useApiVersionQuery.
frontend/src/bindings/index.ts Export GetVersionResponse binding.
frontend/pnpm-lock.yaml Add semver + typings lock entries.
frontend/package.json Add semver + @types/semver.
api/tests/utils.rs Add TestServiceAccount helper + creation helper for tests.
api/tests/setup.rs Update route setup call to accept &[ClientType].
api/tests/api/workspace/rbac/permissions/service_account.rs New RBAC permission tests for service-account permissions.
api/tests/api/workspace/rbac/permissions/mod.rs Register new service-account permission test module.
api/tests/api/workspace/mod.rs Register new workspace service-account test module.
api/tests/api/workspace/deployment/mod.rs Fix request path to include required metric parameter.
api/src/utils/layers/registry/authenticator.rs Update token auth to new client-type resolution signature.
api/src/utils/layers/authenticator.rs Move ClientType into models; enforce endpoint client-type allowlist post-auth.
api/src/utils/extensions/router_ext.rs Change mounting to accept [ClientType] and mount only when allowlists overlap.
api/src/routes/mod.rs Enable API routes for ApiToken + ServiceAccount.
api/src/routes/mimir.patr.cloud/auth.rs Restrict push auth to service-account client type.
api/src/routes/loki.patr.cloud/auth.rs Restrict push auth to service-account client type.
api/src/routes/app.patr.cloud/mod.rs Update setup call to accept &[ClientType::WebDashboard].
api/src/routes/api.patr.cloud/workspace/volume/mod.rs Propagate allowed_client_types: &[ClientType] through workspace volume router.
api/src/routes/api.patr.cloud/workspace/static_site/mod.rs Propagate allowed_client_types: &[ClientType] through static-site router.
api/src/routes/api.patr.cloud/workspace/service_account/create_service_account.rs Implement service-account creation + token generation.
api/src/routes/api.patr.cloud/workspace/service_account/list_service_accounts.rs Implement listing service accounts (with role population).
api/src/routes/api.patr.cloud/workspace/service_account/get_service_account_info.rs Implement service-account detail endpoint.
api/src/routes/api.patr.cloud/workspace/service_account/update_service_account.rs Implement updating name/description and role replacement + cache invalidation.
api/src/routes/api.patr.cloud/workspace/service_account/delete_service_account.rs Implement delete flow + cache invalidation.
api/src/routes/api.patr.cloud/workspace/service_account/regenerate_service_account_token.rs Implement token regeneration + cache invalidation.
api/src/routes/api.patr.cloud/workspace/service_account/mod.rs Wire service-account routes into workspace API router.
api/src/routes/api.patr.cloud/workspace/secret/mod.rs Propagate allowed_client_types: &[ClientType] through secrets router.
api/src/routes/api.patr.cloud/workspace/runner/stream_runner_data_for_workspace.rs Require handshake, persist runner version, and rename required message.
api/src/routes/api.patr.cloud/workspace/runner/mod.rs Propagate allowed_client_types: &[ClientType] through runner router.
api/src/routes/api.patr.cloud/workspace/runner/list_runners_for_workspace.rs Return runner version parsed as semver.
api/src/routes/api.patr.cloud/workspace/runner/get_runner_info.rs Return runner last_seen + version (semver).
api/src/routes/api.patr.cloud/workspace/runner/add_runner_to_workspace.rs Backfill runner version with 0.0.0 for new runners until handshake.
api/src/routes/api.patr.cloud/workspace/rbac/user/mod.rs Propagate allowed_client_types: &[ClientType] through RBAC user router.
api/src/routes/api.patr.cloud/workspace/rbac/role/mod.rs Propagate allowed_client_types: &[ClientType] through RBAC role router.
api/src/routes/api.patr.cloud/workspace/rbac/permission/mod.rs Propagate allowed_client_types: &[ClientType] through RBAC permission router.
api/src/routes/api.patr.cloud/workspace/rbac/mod.rs Propagate allowed_client_types: &[ClientType] through workspace RBAC router.
api/src/routes/api.patr.cloud/workspace/mod.rs Wire service-account router and propagate allowed_client_types across workspace.
api/src/routes/api.patr.cloud/workspace/managed_url/update_managed_url.rs Minor query refactor (remove unused binding).
api/src/routes/api.patr.cloud/workspace/managed_url/mod.rs Propagate allowed_client_types: &[ClientType] through managed-url router.
api/src/routes/api.patr.cloud/workspace/domain/mod.rs Propagate allowed_client_types: &[ClientType] through domain router.
api/src/routes/api.patr.cloud/workspace/deployment/mod.rs Propagate allowed_client_types: &[ClientType] through deployment router.
api/src/routes/api.patr.cloud/workspace/deployment/deploy_history/mod.rs Propagate allowed_client_types: &[ClientType] through deploy-history router.
api/src/routes/api.patr.cloud/workspace/database/mod.rs Propagate allowed_client_types: &[ClientType] through database router.
api/src/routes/api.patr.cloud/workspace/container_registry/mod.rs Propagate allowed_client_types: &[ClientType] through container-registry router.
api/src/routes/api.patr.cloud/user/web_logins/mod.rs Propagate allowed_client_types: &[ClientType] through web-logins router.
api/src/routes/api.patr.cloud/user/recovery_options/mod.rs Propagate allowed_client_types: &[ClientType] through recovery-options router.
api/src/routes/api.patr.cloud/user/mod.rs Propagate allowed_client_types: &[ClientType] through user router.
api/src/routes/api.patr.cloud/user/mfa/mod.rs Propagate allowed_client_types: &[ClientType] through MFA router.
api/src/routes/api.patr.cloud/user/mfa/get_mfa_secret.rs Enforce human identity (username) requirement via IdentityData.
api/src/routes/api.patr.cloud/user/mfa/deactivate_mfa.rs Enforce human identity (username) requirement via IdentityData.
api/src/routes/api.patr.cloud/user/mfa/activate_mfa.rs Enforce human identity (username) requirement via IdentityData.
api/src/routes/api.patr.cloud/user/change_password.rs Enforce human identity (username) requirement via IdentityData.
api/src/routes/api.patr.cloud/user/api_token/mod.rs Propagate allowed_client_types: &[ClientType] through API-token router.
api/src/routes/api.patr.cloud/mod.rs Add /version endpoint and propagate allowed_client_types through API router.
api/src/routes/api.patr.cloud/get_version.rs Implement /version response using semver parse of build version.
api/src/routes/api.patr.cloud/auth/oauth/mod.rs Propagate allowed_client_types: &[ClientType] through OAuth router.
api/src/routes/api.patr.cloud/auth/mod.rs Propagate allowed_client_types: &[ClientType] through auth router.
api/src/models/permissions/web_dashboard.rs Build RequestUserData using IdentityData::User + ClientType::WebDashboard.
api/src/models/permissions/mod.rs Resolve client type by token format; extend permission resolution for service accounts.
api/src/models/permissions/api_token.rs Add service-account token authentication branch; set IdentityData + ClientType.
api/src/migrations/v0_18_0/mod.rs Register service-account and runner-version migrations.
api/src/migrations/v0_18_0/m004_runner_version.rs Add runner.version column, backfill, drop default.
api/src/lib.rs Update prelude to re-export ClientType from models.
api/src/db/workspace/service_account.rs Create service-account tables, indexes, and constraints.
api/src/db/workspace/runner.rs Add version column to runner table schema.
api/src/db/workspace/mod.rs Initialize new service-account DB objects during workspace schema init.
api/src/db/rbac.rs Extend RBAC SQL/constraints to support service-account role permissions.
api/src/app.rs Run API domain with [ApiToken, ServiceAccount] allowed client types.
Cargo.lock Add Rust semver dependency with serde support.
.sqlx/query-fdfefb758976cb8b60e93ed953cdba875bed339457343dc0f5924ff98349746e.json New sqlx metadata for service-account select query.
.sqlx/query-f9827413c755c57d97ff3adea8649a6e5e8c1f7344a559e485099877c3f4acb8.json Update sqlx metadata for include-permissions query (service-account union + params).
.sqlx/query-f57623f566034c7d8f4bcb4a3d9c6d8f9ad46c7829e9962c7e147f4754fb28c2.json New sqlx metadata for service-account FK constraints.
.sqlx/query-f2d50e2d3c72f29923a689013ba9d389c5650ea0987013450ee7acd13701063b.json New sqlx metadata for service-account-role FK constraints.
.sqlx/query-ee6a287cd21311680135dca639cc93f49ed8b3d9081ce8e4e3f678b3491ac1c0.json Update sqlx metadata for runner table (add version column).
.sqlx/query-ed280ab6fd48207bd6f1f0eab21b5fc2f0e09bb6713e8733b76d982690d99d87.json New sqlx metadata for updated RBAC function (service-account unions).
.sqlx/query-e99bbb100490d2884223cf128fc003767b9eb107d8f879d9a11a77a910c4e1c1.json New sqlx metadata for service-account token lookup query.
.sqlx/query-e52764a5ee74a45b231c088fa1a6c247179d54e28ac91810882576ed9c0da604.json New sqlx metadata for resource soft-delete update query.
.sqlx/query-cee64b83d74b8cbebe360a27b935391487e739616aa4dfae5515880fccc14c59.json New sqlx metadata for service-account unique index creation.
.sqlx/query-cecd6c29cde5b95361c8f789e0aa6fd63a688163d3baea3e27f6a1e4eceab4b1.json New sqlx metadata for service-account hard delete query.
.sqlx/query-cd83bf67cbad60094f70337b9da90cac9a45a438c2352dc287a5accfcbe6b470.json Update sqlx metadata for user API token lookup (typed UUID fields).
.sqlx/query-b8f017fb23fe9903ffafbdea8627268ab686ee1c65354032b403801352bdceff.json New sqlx metadata for service-account-role PK constraint.
.sqlx/query-aed0d288a5a92bceeb7e3527753d07390de655789e0df3215b287d0418f1b1fb.json New sqlx metadata for runner version update query.
.sqlx/query-ae0fbfb614fdc8a80fc727b7eb505c0163047eb18f745dcd2e567e34567847e1.json New sqlx metadata for service-account-role delete-by-SA query.
.sqlx/query-9b267699a93e6ab044e820b4f23cfa5c972e9f2e0a6abcc0887e10615eb58113.json Update sqlx metadata for exclude-permissions query (service-account union + params).
.sqlx/query-9a7d77b5c98a4f17854f8c902a0ece01a444a8a83729d2f46d92779359f632d2.json New sqlx metadata for service-account token-hash update query.
.sqlx/query-7a1d96c754b19ce8b852bcfc3febea3dedcce0b113485a9424f3e7a16088a0bc.json Update sqlx metadata for updated workspace_user FK constraint.
.sqlx/query-78701ce75bba88f7cdd8c2f9890cedbfbd915a3acbd1f034f0d15689152192a9.json Remove obsolete sqlx metadata for prior RBAC function definition.
.sqlx/query-75f0921e043d0aa93c127618e535bf8fc39381bc8528ec5a351404e8f4193659.json New sqlx metadata for service-account PK/unique constraints.
.sqlx/query-7136b9c3a1d9939fc2c61535ac2c53aa7f06d1dfda8e5493f548c354ec1756e6.json New sqlx metadata for service-account table creation.
.sqlx/query-6be5bb0ed0f21c53402d53dac32fc05657f328d83ec142f2f2f9c2e2475976c7.json Update sqlx metadata for runner table creation (add version).
.sqlx/query-6989f17c307d10679d7dfbb49ca74340522394ee5512ad1316aece55311e48f6.json Update sqlx metadata for runner listing query (select version).
.sqlx/query-6411b0ea49b4bc13ec3d9f3972f4d95d49b2855f707787ceede181f0ffd67b8d.json New sqlx metadata for service-account list query.
.sqlx/query-63b3d3277e9994237d310bffaa3851ebd0877401f2d4fa0b18b5044b07558cac.json New sqlx metadata for service-account insert query.
.sqlx/query-5cd0f65ae71076847f2b9b08dc4e429ea036f13d8941f579dda4f2457f6f758d.json Update sqlx metadata for role constraints (add unique(id, owner_id)).
.sqlx/query-590deec508ca78e9eac846e8b98a0b01191c730536fdee9d40bd64d5f4d49c4e.json New sqlx metadata for service-account-role table creation.
.sqlx/query-52703699e7152a1459a60c689f4e7ab745665e62a519fc83d85745c246041409.json New sqlx metadata for service-account update query.
.sqlx/query-4e2a186ec7b34027195a6800df7a36dbbde1043bd2c894fd539c8947bfedaeee.json Update sqlx metadata for runner table describe (add version).
.sqlx/query-1830c329cf3f633cd66977924ad52248245c8758bbe23caebcaca45229ea2b23.json New sqlx metadata for resource insert for service accounts.
.sqlx/query-125238af7f9fed6327ea0cbd05ba5a56ef3bfbfebb8ffda652e7ba506be1d1ba.json Update sqlx metadata for runner connect update (set version).
.sqlx/query-11f23e4d57bbe6551e9f909192bfba466207f13075d4e2ddecb34f47eeef5d16.json New sqlx metadata for service-account-role insert query.
.sqlx/query-0e3cfd588fd9494c65cea36970df7a72f67981bbffa31b0b89484dccfc4ad3ed.json New sqlx metadata for service-account role list query.
.sqlx/query-0ddd8016a998bbad16fa15cab795ee1b5181162a640ad43930861f0ed9011577.json Update sqlx metadata for runner insert (backfill version).
Files not reviewed (25)
  • .sqlx/query-0e3cfd588fd9494c65cea36970df7a72f67981bbffa31b0b89484dccfc4ad3ed.json: Language not supported
  • .sqlx/query-11f23e4d57bbe6551e9f909192bfba466207f13075d4e2ddecb34f47eeef5d16.json: Language not supported
  • .sqlx/query-1830c329cf3f633cd66977924ad52248245c8758bbe23caebcaca45229ea2b23.json: Language not supported
  • .sqlx/query-4e2a186ec7b34027195a6800df7a36dbbde1043bd2c894fd539c8947bfedaeee.json: Language not supported
  • .sqlx/query-52703699e7152a1459a60c689f4e7ab745665e62a519fc83d85745c246041409.json: Language not supported
  • .sqlx/query-590deec508ca78e9eac846e8b98a0b01191c730536fdee9d40bd64d5f4d49c4e.json: Language not supported
  • .sqlx/query-63b3d3277e9994237d310bffaa3851ebd0877401f2d4fa0b18b5044b07558cac.json: Language not supported
  • .sqlx/query-6411b0ea49b4bc13ec3d9f3972f4d95d49b2855f707787ceede181f0ffd67b8d.json: Language not supported
  • .sqlx/query-7136b9c3a1d9939fc2c61535ac2c53aa7f06d1dfda8e5493f548c354ec1756e6.json: Language not supported
  • .sqlx/query-75f0921e043d0aa93c127618e535bf8fc39381bc8528ec5a351404e8f4193659.json: Language not supported
  • .sqlx/query-78701ce75bba88f7cdd8c2f9890cedbfbd915a3acbd1f034f0d15689152192a9.json: Language not supported
  • .sqlx/query-9a7d77b5c98a4f17854f8c902a0ece01a444a8a83729d2f46d92779359f632d2.json: Language not supported
  • .sqlx/query-ae0fbfb614fdc8a80fc727b7eb505c0163047eb18f745dcd2e567e34567847e1.json: Language not supported
  • .sqlx/query-aed0d288a5a92bceeb7e3527753d07390de655789e0df3215b287d0418f1b1fb.json: Language not supported
  • .sqlx/query-b8f017fb23fe9903ffafbdea8627268ab686ee1c65354032b403801352bdceff.json: Language not supported
  • .sqlx/query-cecd6c29cde5b95361c8f789e0aa6fd63a688163d3baea3e27f6a1e4eceab4b1.json: Language not supported
  • .sqlx/query-cee64b83d74b8cbebe360a27b935391487e739616aa4dfae5515880fccc14c59.json: Language not supported
  • .sqlx/query-e52764a5ee74a45b231c088fa1a6c247179d54e28ac91810882576ed9c0da604.json: Language not supported
  • .sqlx/query-e99bbb100490d2884223cf128fc003767b9eb107d8f879d9a11a77a910c4e1c1.json: Language not supported
  • .sqlx/query-ed280ab6fd48207bd6f1f0eab21b5fc2f0e09bb6713e8733b76d982690d99d87.json: Language not supported
  • .sqlx/query-ee6a287cd21311680135dca639cc93f49ed8b3d9081ce8e4e3f678b3491ac1c0.json: Language not supported
  • .sqlx/query-f2d50e2d3c72f29923a689013ba9d389c5650ea0987013450ee7acd13701063b.json: Language not supported
  • .sqlx/query-f57623f566034c7d8f4bcb4a3d9c6d8f9ad46c7829e9962c7e147f4754fb28c2.json: Language not supported
  • .sqlx/query-fdfefb758976cb8b60e93ed953cdba875bed339457343dc0f5924ff98349746e.json: Language not supported
  • frontend/pnpm-lock.yaml: Language not supported

Comment on lines +87 to +105
// Populate roles for each service account
for service_account in &mut service_accounts {
service_account.data.roles = query!(
r#"
SELECT
role_id AS "role_id: Uuid"
FROM
service_account_role
WHERE
service_account_id = $1;
"#,
service_account.id as _,
)
.fetch_all(&mut **database)
.await?
.into_iter()
.map(|row| row.role_id)
.collect::<Vec<_>>();
}
Copy link

Copilot AI Apr 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

list_service_accounts populates roles with a per-service-account query (N+1). This will scale poorly as the number of service accounts grows. Consider fetching roles for all returned service accounts in a single query (e.g., join + GROUP BY/array_agg, or a second query with WHERE service_account_id = ANY($1) and then map/group in memory).

Copilot uses AI. Check for mistakes.
Service accounts are a new identity type alongside users. They belong to a
workspace, have a single token (patrv1.{refresh}.{sa_id}), and can be
assigned roles for fine-grained RBAC permissions.

Schema: service_account table with token_hash, service_account_role for role
assignments with workspace-enforced FKs. Updated RESOURCES_WITH_PERMISSION
PL/pgSQL function and permission resolution queries with UNION ALL branches.

Auth: api_token.rs tries user_api_token first, then service_account.
RequestUserData refactored with IdentityData enum (User | ServiceAccount).

CRUD endpoints: create (returns token), list, get, update, delete,
regenerate-token. All use ResourcePermissionAuthenticator with new
ServiceAccountPermission variants.

Also fixes: SearchType derive in macros (syn::Path compat), role FK
workspace enforcement on workspace_user.
- move ClientType to models crate, add ServiceAccount variant
- replace api=bool with client_type=[WebDashboard, ApiToken, ServiceAccount] arrays
- make client_type required in declare_api_endpoint and declare_stream_endpoint macros
- auth layer dispatches by token format (patrv1 prefix vs JWT), checks resolved type against endpoint's ALLOWED_CLIENT_TYPES
- add client_type field to RequestUserData
- restrict loki/mimir push and stream_runner_data_for_workspace to SA only
- exclude SA from create_workspace, SA management, and user-personal endpoints
- add test plan for the refactor to api/tests/TODOs.md
- report runner version on the websocket handshake, store it, flag outdated runners in the UI
- rename SetRunnerExposureType to Handshake, carry version alongside exposure type
- wire up GET /version (was declared but unmounted)
- runner detail: metrics default tab, container-registry-style identity block, delete when disconnected
- migration adds version column with 0.0.0 sentinel for existing rows
…scoped to /workspace/{id}/runner/link/...

- per-runner service account issued on approve, cascading delete with the runner
- redis key for the link includes workspace_id, so cross-workspace lookups are 404
- CLI: workspace picker pre-selected on current_workspace, bearer auth on create/verify, friendly error when link is gone
- CLI: read response body as ApiErrorResponseBody on non-2xx instead of squashing to InternalServerError
- CLI: explicit config (CONFIG_PATH or repo cli.json) now overrides OS-level file
- frontend: split setup page into setup/ folder with index + dash-prefixed components
- frontend: OtpInput generalized (length, sanitize, separator, unstyled, refs not ids)
- frontend: Alert gains align prop
- volume: GetVolumeInfo now requires Volume(View) instead of Volume(Delete)
@rakshith-ravi rakshith-ravi force-pushed the feature/service-accounts branch from 72394f5 to 2e1a820 Compare April 28, 2026 05:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants