Three Go services sharing a Postgres event store and a Valkey/Redis task queue. Only the control server writes to Postgres. The gateway is stateless (no DB, no credentials). The indexer is read-only.
The ControlService exposes 164 RPCs. Every RPC handler in server/internal/api/ validates input at the boundary (proto interceptor) AND at the handler level, enforces authorization, and appends immutable events.
Authentication (7 RPCs)
RPC
Handler file
Register
registration_handler.go
Login
auth_handler.go
RefreshToken
auth_handler.go
Logout
auth_handler.go
GetCurrentUser
auth_handler.go
RenewCertificate
certificate_handler.go
VerifyLoginTOTP
totp_handler.go
TOTP / 2FA (6 RPCs)
RPC
Handler file
SetupTOTP
totp_handler.go
VerifyTOTP
totp_handler.go
DisableTOTP
totp_handler.go
AdminDisableUserTOTP
totp_handler.go
GetTOTPStatus
totp_handler.go
RegenerateBackupCodes
totp_handler.go
SSO / Identity Providers (11 RPCs)
RPC
Handler file
ListAuthMethods
sso_handler.go
GetSSOLoginURL
sso_handler.go
SSOCallback
sso_handler.go
CreateIdentityProvider
idp_handler.go
GetIdentityProvider
idp_handler.go
ListIdentityProviders
idp_handler.go
UpdateIdentityProvider
idp_handler.go
DeleteIdentityProvider
idp_handler.go
ListIdentityLinks
identity_link_handler.go
UnlinkIdentity
identity_link_handler.go
SCIM (3 RPCs)
RPC
Handler file
EnableSCIM
idp_handler.go
DisableSCIM
idp_handler.go
RotateSCIMToken
idp_handler.go
Users (12 RPCs)
RPC
Handler file
CreateUser
user_handler.go
GetUser
user_handler.go
ListUsers
user_handler.go
UpdateUserEmail
user_handler.go
UpdateUserPassword
user_handler.go
SetUserDisabled
user_handler.go
UpdateUserProfile
user_handler.go
UpdateUserLinuxUsername
user_handler.go
AddUserSshKey
user_handler.go
RemoveUserSshKey
user_handler.go
UpdateUserSshSettings
user_handler.go
DeleteUser
user_handler.go
Devices (8 RPCs)
RPC
Handler file
ListDevices
device_handler.go
GetDevice
device_handler.go
SetDeviceLabel
device_handler.go
RemoveDeviceLabel
device_handler.go
AssignDevice
device_handler.go
UnassignDevice
device_handler.go
ListDeviceAssignees
device_handler.go
SetDeviceSyncInterval
device_handler.go
DeleteDevice
device_handler.go
Tokens (5 RPCs)
RPC
Handler file
CreateToken
token_handler.go
GetToken
token_handler.go
ListTokens
token_handler.go
RenameToken
token_handler.go
SetTokenDisabled
token_handler.go
DeleteToken
token_handler.go
Actions (8 RPCs)
RPC
Handler file
CreateAction
action_handler.go, action_crud.go
GetAction
action_handler.go
ListActions
action_handler.go
RenameAction
action_handler.go
UpdateActionDescription
action_handler.go
UpdateActionParams
action_handler.go
DeleteAction
action_handler.go
Action Sets (10 RPCs)
RPC
Handler file
CreateActionSet
action_set_handler.go
GetActionSet
action_set_handler.go
ListActionSets
action_set_handler.go
RenameActionSet
action_set_handler.go
UpdateActionSetDescription
action_set_handler.go
UpdateActionSetSchedule
action_set_handler.go
DeleteActionSet
action_set_handler.go
AddActionToSet
action_set_handler.go
RemoveActionFromSet
action_set_handler.go
ReorderActionInSet
action_set_handler.go
Definitions (11 RPCs)
RPC
Handler file
CreateDefinition
definition_handler.go
GetDefinition
definition_handler.go
ListDefinitions
definition_handler.go
RenameDefinition
definition_handler.go
UpdateDefinitionDescription
definition_handler.go
UpdateDefinitionSchedule
definition_handler.go
DeleteDefinition
definition_handler.go
AddActionSetToDefinition
definition_handler.go
RemoveActionSetFromDefinition
definition_handler.go
ReorderActionSetInDefinition
definition_handler.go
Device Groups (14 RPCs)
RPC
Handler file
CreateDeviceGroup
device_group_handler.go
GetDeviceGroup
device_group_handler.go
ListDeviceGroups
device_group_handler.go
ListDeviceGroupsForDevice
device_group_handler.go
RenameDeviceGroup
device_group_handler.go
UpdateDeviceGroupDescription
device_group_handler.go
UpdateDeviceGroupQuery
device_group_handler.go
DeleteDeviceGroup
device_group_handler.go
AddDeviceToGroup
device_group_handler.go
RemoveDeviceFromGroup
device_group_handler.go
ValidateDynamicQuery
device_group_handler.go
EvaluateDynamicGroup
device_group_handler.go
SetDeviceGroupSyncInterval
device_group_handler.go
SetDeviceGroupMaintenanceWindow
device_group_handler.go
Assignments (7 RPCs)
RPC
Handler file
CreateAssignment
assignment_handler.go
DeleteAssignment
assignment_handler.go
ListAssignments
assignment_handler.go
GetDeviceAssignments
assignment_handler.go
GetUserAssignments
assignment_handler.go
SetUserSelection
user_selection_handler.go
Dispatch (10 RPCs)
RPC
Handler file
ListAvailableActions
action_dispatch.go
DispatchAction
action_dispatch.go
DispatchToMultiple
action_dispatch.go
DispatchAssignedActions
action_dispatch.go
DispatchActionSet
action_dispatch.go
DispatchDefinition
action_dispatch.go
DispatchToGroup
action_dispatch.go
DispatchInstantAction
action_dispatch.go
CancelExecution
action_dispatch.go
GetExecution
action_dispatch.go
ListExecutions
action_dispatch.go
Audit (1 RPC)
RPC
Handler file
ListAuditEvents
audit_handler.go
LUKS / Secrets (5 RPCs)
RPC
Handler file
GetDeviceLpsPasswords
internal_handler.go (proxy)
GetDeviceLuksKeys
internal_handler.go (proxy)
CreateLuksToken
luks_action.go
RevokeLuksDeviceKey
device_handler.go
OSQuery / Inventory (4 RPCs)
RPC
Handler file
DispatchOSQuery
osquery_handler.go
GetOSQueryResult
osquery_handler.go
GetDeviceInventory
osquery_handler.go
RefreshDeviceInventory
osquery_handler.go
Logs (2 RPCs)
RPC
Handler file
QueryDeviceLogs
logs_handler.go
GetDeviceLogResult
logs_handler.go
Roles (7 RPCs)
RPC
Handler file
CreateRole
role_handler.go
GetRole
role_handler.go
ListRoles
role_handler.go
UpdateRole
role_handler.go
DeleteRole
role_handler.go
AssignRoleToUser
role_handler.go
RevokeRoleFromUser
role_handler.go
ListPermissions
role_handler.go
User Groups (14 RPCs)
RPC
Handler file
CreateUserGroup
user_group_handler.go
GetUserGroup
user_group_handler.go
ListUserGroups
user_group_handler.go
UpdateUserGroup
user_group_handler.go
DeleteUserGroup
user_group_handler.go
AddUserToGroup
user_group_handler.go
RemoveUserFromGroup
user_group_handler.go
AssignRoleToUserGroup
user_group_handler.go
RevokeRoleFromUserGroup
user_group_handler.go
ListUserGroupsForUser
user_group_handler.go
UpdateUserGroupQuery
user_group_handler.go
ValidateUserGroupQuery
user_group_handler.go
EvaluateDynamicUserGroup
user_group_handler.go
SetUserGroupMaintenanceWindow
user_group_handler.go
Compliance (9 RPCs)
RPC
Handler file
GetDeviceCompliance
compliance_handler.go
CreateCompliancePolicy
compliance_policy_handler.go
GetCompliancePolicy
compliance_policy_handler.go
ListCompliancePolicies
compliance_policy_handler.go
RenameCompliancePolicy
compliance_policy_handler.go
UpdateCompliancePolicyDescription
compliance_policy_handler.go
DeleteCompliancePolicy
compliance_policy_handler.go
AddCompliancePolicyRule
compliance_policy_handler.go
RemoveCompliancePolicyRule
compliance_policy_handler.go
UpdateCompliancePolicyRule
compliance_policy_handler.go
GetDeviceCompliancePolicyStatus
compliance_handler.go
Search (2 RPCs)
RPC
Handler file
Search
search_handler.go
RebuildSearchIndex
search_handler.go
Settings (4 RPCs)
RPC
Handler file
GetServerSettings
settings_handler.go
UpdateServerSettings
settings_handler.go
SetUserProvisioningEnabled
settings_handler.go
Terminal (4 RPCs)
RPC
Handler file
StartTerminal
terminal_handler.go
StopTerminal
terminal_handler.go
ListActiveTerminalSessions
terminal_handler.go
TerminateTerminalSession
terminal_handler.go
Event type catalog
93 event types recorded in the events table. Each event type has a corresponding payload struct in internal/eventtypes/payloads/ that serves as the single source of truth for the JSON wire format. Postgres triggers project these into *_projection tables.
System roles (Admin, User), system actions, admin policy seeds
009
role_permission_split_7.sql
Permission column split for granular RBAC
010
role_grant_scope_7.sql
:self / :assigned scope grants
011
events_append_only.sql
REVOKE on events table, trigger hardening
012
idp_trust_email_assertions.sql
IdP email assertion trust flag
013
luks_token_hash.sql
LUKS token hash column
014
reconciler_owned_role_permissions.sql
Reconciler role permission ownership
Projection tables
Each domain aggregate has a projection table with a corresponding *_projection naming convention. Go-side projector listeners (internal/projectors/) react to committed events for cross-cutting concerns.
Internal package map (detailed)
internal/api/ — Control server RPC handlers (52 files)
File
Purpose
action_crud.go
Action create/read/update/delete
action_dispatch.go
Dispatch actions to devices/groups
action_handler.go
Action handler constructor + wiring
action_params.go
Action parameter validation
action_schedule.go
Action scheduling (deferred dispatch)
action_set_handler.go
Action set CRUD + member management
action_validators.go
Per-action-type parameter validators
admin_guard.go
System Admin role protection (can't delete/rename/revoke Admin)
Test infrastructure — Postgres containers, factories
internal/actionparams
Action parameter construction helpers
internal/asynqutil
Asynq utility functions
internal/eventtypes
Event type payload structs (93 types) + types.go constants
Invariants
No context.Background() in request paths. Two known findings in background goroutines (terminal_revocation_listener.go:96, settings_handler.go:128) — queued for lifecycle-context injection fix.
Every proto field crossing a trust boundary carries @gotags validate tag.
Every handler validates at boundary (interceptor) + handler level.
Every .catch() logs at minimum debug level.
No secrets in log fields.
All crypto calls carry domain-separation info tags.
Every mutation has owner-scoped WHERE clause.
Non-owner access returns NotFound, never PermissionDenied.
Every state-changing RPC is audit-logged.
IDs are ULIDs. Never crypto.randomUUID().
Never math/rand for cryptographic purposes.
Generated code regenerated from source, never hand-edited.
Events are append-only. REVOKE on events table, trigger-enforced.
Postgres as single writer. Only the control server writes. Gateway and indexer are read-only or have no DB access.
ADR index
23 Architecture Decision Records in server/docs/adr/: