Protocol Documentation
Table of Contents
housecarl_lib/proto/housecarl.proto
CheckAuthorizationRequest
CheckAuthorizationRequest evaluates if an action is allowed
This is the core authorization check that evaluates policies against a request. The request context is a map of string key-value pairs that describe the authorization request.
REQUIRED CONTEXT KEYS:
- "subject": The entity performing the action (e.g., "user:alice@example.com")
- "action": The operation being attempted (e.g., "read", "write", "delete")
- "object": The resource in hc:// URI format (e.g., "hc://550e8400-e29b-41d4-a716-446655440000/documents/report.pdf")
The "object" value MUST be a valid hc:// URI where the domain UUID is used to identify which domain's policies apply. The domain UUID must exist and be accessible by the tenant in the JWT token.
OPTIONAL CONTEXT KEYS:
You can include any additional context attributes that your policies need:
- "time": Request timestamp (RFC3339 format)
- "ip_address": Client IP address
- "user_agent": Client user agent string
- "owner": Resource owner identifier
- "department": User's department
- "clearance_level": Security clearance
- Any custom application-specific attributes
EVALUATION PROCESS:
- Extract domain UUID from object hc:// URI
- Retrieve domain and all superior domains
- Collect all policies from domain hierarchy
- Evaluate each policy against context using policy's engine
- Apply invert and deny flags
- Return authorized=true if at least one allow and no denies
- Return authorized=false if any deny or no allows (default deny)
EXAMPLE REQUEST:
{ "context": { "subject": "user:alice@example.com", "action": "read", "object": "hc://550e8400-e29b-41d4-a716-446655440000/documents/report.pdf", "time": "2024-01-15T10:30:00Z", "ip_address": "192.168.1.100", "owner": "user:alice@example.com" } }
This would match a policy like: { "engine": EVALUATION_ENGINE_PREFIX, "statements": [{ "rules": { "action": "read", "object": "hc://550e8400-e29b-41d4-a716-446655440000/documents/" } }] }
POLICY MACRO EVALUATION:
If policies contain macros (e.g., $current_user()), they are evaluated using information from:
- JWT token (user_id, tenant_id)
- Request context (custom attributes)
- System state (current time, etc.)
For example, a policy with: {"subject": "$current_user()", "owner": "$resource_owner()"} Would expand at evaluation time to: {"subject": "user:alice@example.com", "owner": "user:alice@example.com"} And match if both values are equal.
ERROR CASES:
Returns gRPC error if:
- Missing required context keys (subject, action, object)
- Invalid hc:// URI format in object
- Domain UUID not found or not accessible
- JWT token invalid or missing
- Tenant in JWT doesn't match domain's tenant
| Field | Type | Label | Description |
|---|---|---|---|
| context | CheckAuthorizationRequest.ContextEntry | repeated | Authorization context (key-value pairs, supporting multi-valued attributes) REQUIRED keys: - subject: Entity performing the action - action: Operation being attempted - object: Resource in hc:// URI format OPTIONAL keys: - Any custom attributes your policies need (time, ip_address, owner, etc.) MULTI-VALUE SUPPORT: Each value can be either a single string or an array of strings. Multi-valued attributes use OR semantics during policy matching: A request with group: ["red", "blue"] will match policy statement group: "blue" Example with single values: { "subject": {"single": "user:alice@example.com"}, "action": {"single": "read"}, "object": {"single": "hc://550e8400.../documents/report.pdf"} } Example with multi-valued attribute: { "subject": {"single": "user:alice@example.com"}, "action": {"single": "read"}, "object": {"single": "hc://550e8400.../documents/report.pdf"}, "group": {"multiple": {"values": ["red", "blue", "green"]}} } | |
CheckAuthorizationRequest.ContextEntry
| Field | Type | Label | Description |
|---|---|---|---|
| key | string | ||
| value | RequestValue |
CheckAuthorizationResponse
CheckAuthorizationResponse returns the authorization decision
The authorized field indicates whether the request should be allowed.
INTERPRETATION:
authorized = true:
- At least one policy allowed the request
- No policies denied the request
- The action should be permitted
authorized = false:
- Either no policies matched (default deny)
- Or at least one policy denied the request (deny overrides allow)
- The action should be rejected
APPLICATION INTEGRATION:
After receiving the response, your application should:
- Check the authorized field
- If true, proceed with the requested operation
- If false, reject the operation and return 403 Forbidden to the client
- Log the authorization decision for audit purposes
PERFORMANCE CONSIDERATIONS:
- CheckAuthorization calls are designed to be fast (sub-millisecond typical)
- Results can be cached for short periods (seconds to minutes)
- Cache keys should include all context attributes used in policies
- Invalidate cache when policies are updated
AUDIT LOGGING:
All CheckAuthorization calls should be logged to the audit service with:
- Timestamp
- Subject (from context)
- Action (from context)
- Object (from context)
- Result (authorized true/false)
- Tenant ID (from JWT)
- Request ID (for correlation)
| Field | Type | Label | Description |
|---|---|---|---|
| authorized | bool | True if the action is allowed, false if denied or no policy matched This is the authorization decision. Application MUST enforce this result. | |
ClearUserAttributesRequest
ClearUserAttributesRequest removes all attributes for a user in the current tenant
| Field | Type | Label | Description |
|---|---|---|---|
| user_id | string | UUID - User to clear attributes for |
CreateApiKeyRequest
| Field | Type | Label | Description |
|---|---|---|---|
| user_id | string | UUID - User to create key for |
CreateApiKeyResponse
| Field | Type | Label | Description |
|---|---|---|---|
| api_key | string | JWT token that can be used for API authentication |
CreateDomainRequest
| Field | Type | Label | Description |
|---|---|---|---|
| tenant_id | string | UUID - Tenant that will own the domain | |
| name | string | Domain name, must be unique within tenant | |
| superior_domain_ids | string | repeated | UUIDs - Parent domains for hierarchy |
CreateEmailVerificationTokenRequest
| Field | Type | Label | Description |
|---|---|---|---|
| user_id | string | UUID - User the token is for | |
| string | Email address to verify | ||
| token_type | TokenType | Type of verification token | |
| expiration_hours | uint32 | Token validity period (default 24) |
CreateEmailVerificationTokenResponse
| Field | Type | Label | Description |
|---|---|---|---|
| token | string | Generated verification token (64-char alphanumeric) | |
| expires_at | google.protobuf.Timestamp | Token expiration timestamp |
CreateOAuthUserRequest
| Field | Type | Label | Description |
|---|---|---|---|
| username | string | Username for the new user | |
| string | Email address | ||
| oauth_provider | string | OAuth provider (e.g., "google") | |
| oauth_subject | string | Provider's unique user identifier | |
| email_verified | bool | Whether email is pre-verified by OAuth provider | |
| tenant_id | string | optional | Optional tenant to associate user with |
CreateOAuthUserResponse
| Field | Type | Label | Description |
|---|---|---|---|
| user_id | string | UUID - Created user's identifier |
CreateServiceAccountRequest
| Field | Type | Label | Description |
|---|---|---|---|
| name | string | Service account name (will be prefixed with svc:) | |
| description | string | Human-readable description | |
| tenant_id | string | UUID - Tenant to associate with | |
| expiration_days | uint64 | optional | API key expiration (default: 365 days) |
CreateServiceAccountResponse
| Field | Type | Label | Description |
|---|---|---|---|
| service_account_id | string | UUID - Created service account ID | |
| username | string | Full username (svc:name) | |
| api_key_id | string | UUID - API key ID | |
| string | Generated email (name@service.internal) | ||
| expiration | int64 | Unix epoch when API key expires | |
| token | string | JWT token for immediate authentication (same as Login) |
CreateTenantRequest
Request to create a new tenant. See CreateTenant RPC documentation for details on the bootstrap behavior that occurs when a tenant is created (automatic domain, policies, and user association).
| Field | Type | Label | Description |
|---|---|---|---|
| name | string | Tenant name, must be globally unique across the system. Used for human-readable identification and lookup via GetTenantByName. | |
| description | string | Human-readable description of the tenant's purpose or organization. |
CreateTenantUserAssociationRequest
| Field | Type | Label | Description |
|---|---|---|---|
| tenant_id | string | UUID - Tenant to grant access to | |
| user_id | string | UUID - User to grant access |
CreateUserRequest
| Field | Type | Label | Description |
|---|---|---|---|
| username | string | Must be unique, used for login | |
| string | Must be valid email format | ||
| password | string | Must meet security requirements |
CreateUserResponse
| Field | Type | Label | Description |
|---|---|---|---|
| user_id | string | UUID - Created user's identifier |
DeleteApiKeyRequest
| Field | Type | Label | Description |
|---|---|---|---|
| user_id | string | UUID - User who owns the key | |
| key_id | string | UUID - Key to delete |
DeleteDomainRequest
| Field | Type | Label | Description |
|---|---|---|---|
| tenant_id | string | UUID - Tenant ID for access control | |
| domain_id | string | UUID - Domain to delete |
DeleteServiceAccountRequest
| Field | Type | Label | Description |
|---|---|---|---|
| id | string | UUID - Service account ID to delete |
DeleteTenantRequest
| Field | Type | Label | Description |
|---|---|---|---|
| id | string | UUID - Tenant to delete |
DeleteTenantUserAssociationRequest
| Field | Type | Label | Description |
|---|---|---|---|
| tenant_id | string | UUID - Tenant to revoke access from | |
| user_id | string | UUID - User to revoke access |
DeleteUserRequest
| Field | Type | Label | Description |
|---|---|---|---|
| id | string | UUID - User to delete |
Domain
Domain represents a policy container within a tenant Domains can be hierarchical through superior_domain_ids
| Field | Type | Label | Description |
|---|---|---|---|
| id | string | UUID - Unique domain identifier | |
| name | string | Domain name, unique within tenant | |
| tenant_id | string | UUID - Owning tenant | |
| active | bool | Whether domain is active for policy evaluation | |
| superior_domain_ids | string | repeated | UUIDs - Parent domains in hierarchy |
| policies | Policy | repeated | Policies attached to this domain |
FindUserByOAuthRequest
| Field | Type | Label | Description |
|---|---|---|---|
| oauth_provider | string | OAuth provider (e.g., "google") | |
| oauth_subject | string | Provider's unique user identifier |
FindUserByOAuthResponse
| Field | Type | Label | Description |
|---|---|---|---|
| user_id | string | UUID - User ID if found | |
| found | bool | True if user exists | |
| username | string | Username if found | |
| string | Email if found |
GetApiKeyRequest
| Field | Type | Label | Description |
|---|---|---|---|
| user_id | string | UUID - User who owns the key | |
| key_id | string | UUID - Specific key to retrieve |
GetApiKeyResponse
| Field | Type | Label | Description |
|---|---|---|---|
| api_key | string | JWT token (metadata only, not the secret) |
GetDomainByNameRequest
| Field | Type | Label | Description |
|---|---|---|---|
| tenant_id | string | UUID - Tenant ID for access control | |
| name | string | Domain name to look up |
GetDomainPoliciesRequest
| Field | Type | Label | Description |
|---|---|---|---|
| tenant_id | string | UUID - Tenant ID for access control | |
| domain_id | string | UUID - Domain whose policies to retrieve |
GetDomainPoliciesResponse
| Field | Type | Label | Description |
|---|---|---|---|
| policies | Policy | repeated | All policies attached to the domain |
GetDomainRequest
| Field | Type | Label | Description |
|---|---|---|---|
| tenant_id | string | UUID - Tenant ID for access control | |
| domain_id | string | UUID - Domain to retrieve |
GetServiceAccountRequest
| Field | Type | Label | Description |
|---|---|---|---|
| id | string | UUID - Service account ID |
GetTenantAssociationsRequest
| Field | Type | Label | Description |
|---|---|---|---|
| tenant_id | string | UUID - Tenant to get associations for |
GetTenantAssociationsResponse
| Field | Type | Label | Description |
|---|---|---|---|
| users | User | repeated | All users associated with the tenant |
GetTenantByNameRequest
| Field | Type | Label | Description |
|---|---|---|---|
| name | string | Tenant name to look up |
GetTenantRequest
| Field | Type | Label | Description |
|---|---|---|---|
| id | string | UUID - Tenant identifier |
GetTenantUserAssociationRequest
| Field | Type | Label | Description |
|---|---|---|---|
| tenant_id | string | UUID - Tenant to check | |
| user_id | string | UUID - User to check |
GetTenantUserAssociationResponse
| Field | Type | Label | Description |
|---|---|---|---|
| is_associated | bool | True if user can act on behalf of tenant |
GetUserAttributesRequest
GetUserAttributesRequest retrieves all attributes for a user in the current tenant
| Field | Type | Label | Description |
|---|---|---|---|
| user_id | string | UUID - User to get attributes for |
GetUserAttributesResponse
GetUserAttributesResponse returns all attributes grouped by key
| Field | Type | Label | Description |
|---|---|---|---|
| attributes | GetUserAttributesResponse.AttributesEntry | repeated | All attributes for the user |
GetUserAttributesResponse.AttributesEntry
| Field | Type | Label | Description |
|---|---|---|---|
| key | string | ||
| value | StringArray |
GetUserByNameRequest
| Field | Type | Label | Description |
|---|---|---|---|
| username | string | Username to look up |
GetUserRequest
| Field | Type | Label | Description |
|---|---|---|---|
| id | string | UUID - User to retrieve |
IsLoggedInRequest
JWT will be passed in request headers No body needed - authentication via Bearer token
IsLoggedInResponse
| Field | Type | Label | Description |
|---|---|---|---|
| is_logged_in | bool | True if JWT is valid and session active |
ListServiceAccountsRequest
| Field | Type | Label | Description |
|---|---|---|---|
| tenant_id | string | UUID - Tenant to list accounts for | |
| active_only | bool | optional | Filter to only active accounts (default: false) |
ListServiceAccountsResponse
| Field | Type | Label | Description |
|---|---|---|---|
| service_accounts | ServiceAccount | repeated | |
| total_count | uint32 | Total number of service accounts |
ListTenantsRequest
Empty for now, could add pagination later
ListTenantsResponse
| Field | Type | Label | Description |
|---|---|---|---|
| tenants | Tenant | repeated | All tenants in the system |
ListUsersRequest
Empty for now, could add pagination later
ListUsersResponse
| Field | Type | Label | Description |
|---|---|---|---|
| users | User | repeated | All users in the system |
LoginRequest
| Field | Type | Label | Description |
|---|---|---|---|
| username | string | Username credential | |
| password | string | Password credential | |
| tenant | string | optional | Tenant name - for multi-tenant login |
| duration | uint64 | optional | Token validity duration in seconds |
LoginResponse
| Field | Type | Label | Description |
|---|---|---|---|
| token | string | JWT token for subsequent API calls | |
| user_id | string | UUID - Logged in user | |
| tenant_id | string | optional | UUID - Tenant context for the session |
LogoutRequest
| Field | Type | Label | Description |
|---|---|---|---|
| user_id | string | UUID - Must match JWT claims |
MarkEmailVerifiedRequest
| Field | Type | Label | Description |
|---|---|---|---|
| user_id | string | UUID - User to mark as verified |
Policy
Policy defines authorization rules for resources
A policy is a named set of rules that determine whether a request should be allowed or denied. Policies are attached to domains and evaluated during CheckAuthorization calls.
POLICY EVALUATION:
During CheckAuthorization, each policy is evaluated as follows:
- For each statement in the policy: a. Check if all statement keys exist in request context b. Match statement values against context values using the engine c. If all keys match, the statement matches
- If ANY statement matches: a. Apply invert flag: if true, flip the result (match→no match, no match→match) b. Track as DENY if deny=true, otherwise as ALLOW c. Continue to next policy
- After all policies evaluated: a. If ANY DENY, return authorized=false b. If NO DENY and at least one ALLOW, return authorized=true c. If NO policies matched, return authorized=false (default deny)
POLICY FLAGS:
invert: Reverses the match result
- Use for "everyone except" scenarios
- Example: Allow all users except contractors
- Set invert=true with contractor user pattern
deny: Makes this a deny policy (overrides allows)
- Use for security boundaries and restrictions
- Example: Deny all access to /admin/* for non-admins
- Deny policies always win over allow policies
BEST PRACTICES:
- Keep policies simple and focused on a single concern
- Use descriptive names that indicate the policy's purpose
- Document complex policies in the description field
- Prefer simpler engines (FIXED, PREFIX) over complex ones (REGEX)
- Use deny policies sparingly for security-critical restrictions
- Test policies thoroughly before deployment
POLICY EXAMPLES:
Allow read access to documents: { "name": "read-documents", "description": "Allow users to read documents", "invert": false, "deny": false, "engine": EVALUATION_ENGINE_PREFIX, "statements": [{ "rules": { "action": "read", "object": "hc://550e8400-e29b-41d4-a716-446655440000/documents/" } }] }
Deny access to sensitive resources: { "name": "deny-sensitive", "description": "Prevent access to sensitive resources", "invert": false, "deny": true, "engine": EVALUATION_ENGINE_PREFIX, "statements": [{ "rules": { "object": "hc://550e8400-e29b-41d4-a716-446655440000/sensitive/" } }] }
Allow owner-only access using macros: { "name": "owner-access", "description": "Users can only access resources they own", "invert": false, "deny": false, "engine": EVALUATION_ENGINE_FIXED, "statements": [{ "rules": { "subject": "$current_user()", "owner": "$resource_owner()" } }] }
| Field | Type | Label | Description |
|---|---|---|---|
| name | string | Policy name for identification Must be unique within a domain Used for debugging and audit trails | |
| description | string | Human-readable description Explain the policy's purpose and when it applies Recommended for all policies | |
| invert | bool | Invert the match result false (default): If statements match, apply allow/deny true: If statements match, do NOT apply (implement "except" logic) | |
| deny | bool | Whether this is a deny policy false (default): This is an allow policy true: This is a deny policy (overrides all allow policies) | |
| engine | EvaluationEngine | Evaluation engine for matching rules Determines how statement rules are matched against request context | |
| statements | PolicyStatement | repeated | Policy statements (rules) A policy matches if ANY statement matches Use multiple statements for OR logic (e.g., allow read OR write) |
PolicyStatement
PolicyStatement contains the actual rules to evaluate
A statement is a set of key-value pairs that must ALL match the request context for the statement to match (AND logic).
STATEMENT MATCHING:
For a statement to match:
- Every key in rules must exist in the request context
- Every value must match according to the policy's engine
- Request context may have additional keys (they are ignored)
EXAMPLE:
Policy statement: { "rules": { "action": "read", "object": "hc://550e8400-e29b-41d4-a716-446655440000/documents/" } }
Request context (matches): { "subject": "user:alice@example.com", // ignored (not in rules) "action": "read", // matches "object": "hc://550e8400-e29b-41d4-a716-446655440000/documents/report.pdf" // matches (PREFIX engine) }
Request context (does NOT match): { "action": "write", // does not match (not "read") "object": "hc://550e8400-e29b-41d4-a716-446655440000/documents/report.pdf" }
MULTIPLE STATEMENTS:
Use multiple statements in a policy for OR logic: { "statements": [ {"rules": {"action": "read"}}, {"rules": {"action": "write"}} ] } This matches requests with action="read" OR action="write"
| Field | Type | Label | Description |
|---|---|---|---|
| rules | PolicyStatement.RulesEntry | repeated | Engine-specific rule definitions Keys: Arbitrary context attributes (subject, action, object, time, etc.) Values: Patterns to match (exact, prefix, regex, glob depending on engine) Common context keys: - subject: The entity performing the action - action: The operation (read, write, delete, etc.) - object: The resource (hc:// URI) - time: Request timestamp - ip_address: Client IP - user_agent: Client user agent - custom_key: Any application-specific attribute | |
PolicyStatement.RulesEntry
| Field | Type | Label | Description |
|---|---|---|---|
| key | string | ||
| value | string |
PutDomainPoliciesRequest
| Field | Type | Label | Description |
|---|---|---|---|
| tenant_id | string | UUID - Tenant ID for access control | |
| domain_id | string | UUID - Domain to update policies for | |
| policies | Policy | repeated | Complete policy set (replaces existing) |
RefreshLoginWithTenantRequest
RefreshLoginWithTenantRequest transmutes a tenantless login to a tenantful one
USAGE SCENARIO:
- User logs in without tenant parameter → receives JWT with no tenant_id claim
- User creates a tenant (becomes associated via CreateTenant)
- User calls RefreshLoginWithTenant to upgrade session
- Server validates association and issues new JWT with tenant_id claim
SECURITY:
- Requires valid JWT token in request headers (authentication)
- Current JWT MUST NOT have a tenant_id claim (can only transmute tenantless → tenantful)
- Verifies user is associated with requested tenant via TenantsUserLink
- Returns PERMISSION_DENIED if user is not associated
- Returns FAILED_PRECONDITION if current JWT already has a tenant
RESPONSE:
Returns LoginResponse with new JWT containing:
- Same user_id as current JWT
- tenant_id claim set to requested tenant
- User attributes for the tenant included in JWT
- New expiration timestamp (default 12 hours from now)
| Field | Type | Label | Description |
|---|---|---|---|
| tenant_id | string | UUID or name of tenant to associate the session with User must already be associated with this tenant |
RefreshTenantsRequest
Empty - refreshes tokens for all tenants
RemoveUserAttributeRequest
RemoveUserAttributeRequest removes attribute values for a user in the current tenant
| Field | Type | Label | Description |
|---|---|---|---|
| user_id | string | UUID - User to remove attributes from | |
| attribute_key | string | Attribute key to modify | |
| attribute_values | string | repeated | Specific values to remove (empty = remove all for key) |
RequestValue
RequestValue supports both single and multi-valued context attributes Used in CheckAuthorizationRequest for multi-valued attribute matching
| Field | Type | Label | Description |
|---|---|---|---|
| single | string | Single string value | |
| multiple | StringArray | Multiple values (OR semantics in matching) |
ServiceAccount
| Field | Type | Label | Description |
|---|---|---|---|
| id | string | UUID - Service account ID | |
| username | string | Full username (svc:name) | |
| string | Generated email (name@service.internal) | ||
| tenant_id | string | UUID - Tenant this account is homed to | |
| description | string | Human-readable description | |
| active | bool | Whether account is enabled | |
| created_at | int64 | Unix epoch | |
| api_key_id | string | UUID - Current API key ID | |
| api_key_expiration | int64 | Unix epoch when API key expires | |
| api_key_active | bool | Whether API key is currently valid |
SetUserAttributeRequest
SetUserAttributeRequest adds attribute values for a user in the current tenant
| Field | Type | Label | Description |
|---|---|---|---|
| user_id | string | UUID - User to set attributes for | |
| attribute_key | string | Attribute key (e.g., "group", "role") | |
| attribute_values | string | repeated | Values to add (e.g., ["red", "blue"]) |
StoreOAuthStateRequest
| Field | Type | Label | Description |
|---|---|---|---|
| state | string | Random state string for CSRF protection | |
| tenant_id | string | optional | Optional tenant ID for the OAuth flow |
| expires_in_minutes | int32 | Expiration time in minutes (default 10) | |
| redirect_uri | string | Optional redirect URI after OAuth completion | |
| nonce | string | Optional nonce for additional security |
StringArray
StringArray holds multiple string values for a single key
| Field | Type | Label | Description |
|---|---|---|---|
| values | string | repeated |
Tenant
Tenant represents a top-level organizational unit All resources are isolated by tenant
| Field | Type | Label | Description |
|---|---|---|---|
| id | string | UUID - Unique tenant identifier | |
| name | string | Tenant name, globally unique | |
| description | string | Human-readable description | |
| active | bool | Whether tenant is active | |
| control_plane | Tokens | API call limits for management operations | |
| data_plane | Tokens | API call limits for authorization operations | |
| last_token_reset | google.protobuf.Timestamp | Last token refresh time | |
| subscription | string | Subscription plan identifier | |
| domains | Domain | repeated | Domains owned by this tenant |
Tokens
API call tracking for rate limiting
Housecarl uses a token bucket algorithm for rate limiting based on API calls. Each operation consumes one call, and the call quota is replenished during RefreshTenants operations. "Calls" represents the most technically accurate and user-controllable measure of system usage.
CALL CONSUMPTION:
When an operation is performed:
- If available calls >= 1, decrement by 1 and allow the operation
- If calls < 1 and overage < max_overage, increment overage and allow
- If calls < 1 and overage >= max_overage, reject with RESOURCE_EXHAUSTED
CALL QUOTA REFRESH:
Call RefreshTenants periodically (e.g., hourly, daily) to reset:
- calls = max_calls (replenish to maximum)
- overage = 0 (reset burst usage)
RATE LIMIT TYPES:
Tenants have two separate call quotas:
- control_plane: For management operations (CreateUser, CreateDomain, etc.)
- data_plane: For authorization checks (CheckAuthorization)
Users have a separate quota:
- user_plane: For user-initiated operations
BURST CAPACITY:
max_overage allows temporary bursts above max_calls. This is useful for handling traffic spikes without immediately rejecting requests.
Example: max_calls=1000, max_overage=500
- Steady state: Up to 1000 operations per refresh cycle
- Burst capacity: Up to 1500 operations (1000 + 500 overage)
- After burst: Next refresh resets both call quota and overage
| Field | Type | Label | Description |
|---|---|---|---|
| max_tokens | int64 | Maximum calls available per refresh cycle This is your baseline rate limit quota | |
| tokens | int64 | Current available calls (decremented on each operation) When this reaches 0, overage is used | |
| max_overage | int64 | Maximum allowed overage (burst capacity above max_calls) Set to 0 to disable burst capacity | |
| overage | int64 | Current overage usage (incremented when calls reach 0) Reset to 0 during RefreshTenants |
UpdateDomainRequest
| Field | Type | Label | Description |
|---|---|---|---|
| tenant_id | string | UUID - Must match domain's tenant_id | |
| domain | Domain | Updated domain object |
UpdateServiceAccountRequest
| Field | Type | Label | Description |
|---|---|---|---|
| id | string | UUID - Service account ID | |
| description | string | optional | New description (if provided) |
| active | bool | optional | New active status (if provided) |
UpdateTenantRequest
| Field | Type | Label | Description |
|---|---|---|---|
| tenant | Tenant | Updated tenant object, ID must match existing tenant |
UpdateUserRequest
| Field | Type | Label | Description |
|---|---|---|---|
| id | string | UUID - User to update | |
| username | string | New username (must be unique) | |
| string | New email | ||
| password | string | optional | New password (optional) |
| active | bool | Active status |
User
User represents an authenticated entity that can access tenants
| Field | Type | Label | Description |
|---|---|---|---|
| id | string | UUID - Unique user identifier | |
| username | string | Unique username for login | |
| string | Email address | ||
| active | bool | Whether user can login | |
| api_key_ids | string | repeated | UUIDs - Associated API keys |
| user_plane | Tokens | User-specific API call limits | |
| email_verified | bool | Whether email has been verified |
ValidateEmailVerificationTokenRequest
| Field | Type | Label | Description |
|---|---|---|---|
| token | string | Token to validate | |
| token_type | TokenType | Expected token type |
ValidateEmailVerificationTokenResponse
| Field | Type | Label | Description |
|---|---|---|---|
| valid | bool | True if token is valid | |
| user_id | string | UUID - User ID associated with token | |
| string | Email address associated with token | ||
| error_message | string | Error description if invalid (expired, used, not found, etc.) |
ValidateOAuthStateRequest
| Field | Type | Label | Description |
|---|---|---|---|
| state | string | State string to validate |
ValidateOAuthStateResponse
| Field | Type | Label | Description |
|---|---|---|---|
| valid | bool | True if state is valid and not expired | |
| tenant_id | string | Associated tenant ID | |
| redirect_uri | string | Associated redirect URI (if any) | |
| nonce | string | Associated nonce (if any) | |
| error_message | string | Error description if invalid |
EvaluationEngine
Policy evaluation engine types
The evaluation engine determines how policy statement rules are matched against the request context during authorization checks.
All engines operate on key-value pairs where both keys and values are strings. The request context is matched against policy statement rules according to the selected engine's matching semantics.
MATCHING LOGIC:
For a policy statement to match:
- All keys in the policy statement must exist in the request context
- The values must match according to the engine's rules
- Request context may contain additional keys (they are ignored)
EXAMPLE REQUEST CONTEXT: { "subject": "user:alice@example.com", "action": "read", "object": "hc://550e8400-e29b-41d4-a716-446655440000/documents/report.pdf", "time": "2024-01-15T10:30:00Z", "ip_address": "192.168.1.100" }
| Name | Number | Description |
|---|---|---|
| EVALUATION_ENGINE_UNSPECIFIED | 0 | Unspecified engine - do not use in production policies |
| EVALUATION_ENGINE_FIXED | 1 | FIXED: Exact string match (case-sensitive) Policy statement values must exactly match request context values. Use for: Precise matching of known values Example policy statement: { "subject": "user:alice@example.com", "action": "read" } Matches request context with: subject = "user:alice@example.com" (exact match) action = "read" (exact match) Does NOT match: subject = "user:alice" (not exact) action = "READ" (case-sensitive) | |
| EVALUATION_ENGINE_PREFIX | 2 | PREFIX: Prefix matching (starts with) Request context values must start with policy statement values. Use for: Hierarchical resource paths, user groups, action categories Example policy statement: { "object": "hc://550e8400-e29b-41d4-a716-446655440000/documents/", "action": "read" } Matches request context with: object = "hc://550e8400-e29b-41d4-a716-446655440000/documents/report.pdf" object = "hc://550e8400-e29b-41d4-a716-446655440000/documents/folder/file.txt" Does NOT match: object = "hc://550e8400-e29b-41d4-a716-446655440000/images/photo.jpg" | |
| EVALUATION_ENGINE_REGEX | 3 | REGEX: Regular expression matching Policy statement values are regular expressions matched against request context values using Rust regex syntax. Use for: Complex pattern matching, wildcard domains, time ranges Example policy statement: { "subject": "^user:[a-z]+@example\.com$", "action": "read|write", "time": "^2024-.T(09|10|11|12|13|14|15|16):." } Matches request context with: subject = "user:alice@example.com" (matches regex) action = "write" (matches alternation) time = "2024-01-15T14:30:00Z" (matches time range) WARNING: Complex regex can impact performance. Use PREFIX or GLOB when simpler matching suffices. | |
| EVALUATION_ENGINE_GLOB | 4 | GLOB: Glob pattern matching (shell-style wildcards) Policy statement values use glob patterns (* for any characters, ? for single character) matched against request context values. Use for: Filename patterns, simple wildcards Example policy statement: { "object": "hc://550e8400-e29b-41d4-a716-446655440000/documents/.pdf", "subject": "user:@example.com" } Matches request context with: object = "hc://550e8400-e29b-41d4-a716-446655440000/documents/report.pdf" subject = "user:alice@example.com" Does NOT match: object = "hc://550e8400-e29b-41d4-a716-446655440000/documents/folder/file.pdf" (glob * does not match path separators by default) | |
| EVALUATION_ENGINE_FIRST_ORDER_LOGIC | 5 | FIRST_ORDER_LOGIC: Complex logical expressions Reserved for future use. Supports complex boolean logic, comparisons, and quantifiers. NOT CURRENTLY IMPLEMENTED | |
TokenType
| Name | Number | Description |
|---|---|---|
| TOKEN_TYPE_UNSPECIFIED | 0 | |
| TOKEN_TYPE_EMAIL_VERIFICATION | 1 | |
| TOKEN_TYPE_PASSWORD_RESET | 2 | |
| TOKEN_TYPE_EMAIL_CHANGE | 3 |
HousecarlService
Domain operations Domains are hierarchical policy containers within a tenant
| Method Name | Request Type | Response Type | Description |
|---|---|---|---|
| CreateDomain | CreateDomainRequest | Domain | Creates a new domain for the specified tenant Domain names must be unique within a tenant and can have superior domains for hierarchy |
| GetDomain | GetDomainRequest | Domain | Retrieves a domain for the specified tenant Returns error if domain doesn't exist or belongs to different tenant |
| GetDomainByName | GetDomainByNameRequest | Domain | Retrieves a domain by name for the specified tenant Returns error if domain doesn't exist or is not unique |
| UpdateDomain | UpdateDomainRequest | .google.protobuf.Empty | Updates an existing domain for the specified tenant ID and tenant_id in request body must match URL parameters |
| DeleteDomain | DeleteDomainRequest | .google.protobuf.Empty | Deletes the specified domain Will fail if domain has dependent resources |
| GetDomainPolicies | GetDomainPoliciesRequest | GetDomainPoliciesResponse | Retrieves policies for the specified domain Returns the policy list attached to the domain |
| PutDomainPolicies | PutDomainPoliciesRequest | .google.protobuf.Empty | Updates policies for the specified domain Replaces the entire policy set for the domain |
| CreateTenant | CreateTenantRequest | Tenant | Creates a new tenant with automatic bootstrap configuration. BOOTSTRAP BEHAVIOR: This operation is a bootstrap operation that any authenticated user can perform. No policy-based authorization check is required beyond valid authentication. When a tenant is created, the following resources are automatically provisioned in a single atomic transaction: 1. TENANT: The tenant record is created with the provided name and description. 2. USER ASSOCIATION: The creating user is automatically associated with the new tenant via a tenant-user link. This allows the user to operate within the tenant context. 3. ROOT DOMAIN: A domain named "root" is created as the tenant's primary domain. This domain cannot be renamed (enforced by database trigger) and serves as the default policy evaluation context for the tenant. 4. STARTER POLICIES: Two policies are automatically created in the root domain: a) "starter" policy - Grants the creating user full access to all resources within the tenant. This policy matches: - subject (sub claim): the creating user's UUID - action: any (.+) - object: any housecarl resource (hc://.+) b) "root access" policy - Grants the system root user (from the root tenant) administrative access to this tenant. This enables platform-level administration and support operations. SECURITY CONSIDERATIONS: - The starter policy uses the user's UUID, not username, preventing policy bypass through username changes. - Additional users can be granted access by creating new policies in the root domain or by associating them with the tenant and adding appropriate policy statements. - The creating user should configure appropriate policies before inviting other users to the tenant. Returns: The created Tenant object with its assigned UUID. | |
| GetTenant | GetTenantRequest | Tenant | Retrieves the specified tenant by ID |
| GetTenantByName | GetTenantByNameRequest | Tenant | Gets a tenant by name Tenant names are unique across the system |
| UpdateTenant | UpdateTenantRequest | .google.protobuf.Empty | Updates an existing tenant ID in request body must match the tenant being updated |
| DeleteTenant | DeleteTenantRequest | .google.protobuf.Empty | Deletes the specified tenant Will cascade delete associated resources |
| ListTenants | ListTenantsRequest | ListTenantsResponse | Gets all tenants Admin operation that returns all tenants in the system |
| RefreshTenants | RefreshTenantsRequest | .google.protobuf.Empty | Refreshes call quotas for all tenants Should be called every refresh cycle to reset API call limits Updates control_plane, data_plane call quotas, and resets overage counters |
| CreateUser | CreateUserRequest | CreateUserResponse | Creates a new user Can be called without authentication Username and email must be unique, password must meet security requirements |
| GetUser | GetUserRequest | User | Get user by ID |
| GetUserByName | GetUserByNameRequest | User | Get user by name Username lookup across the system |
| UpdateUser | UpdateUserRequest | User | Updates an existing user Can update email, active status, and other metadata |
| DeleteUser | DeleteUserRequest | .google.protobuf.Empty | Deletes the specified user Will remove all tenant associations and invalidate tokens |
| ListUsers | ListUsersRequest | ListUsersResponse | Get all users Admin operation that returns all users in the system |
| Login | LoginRequest | LoginResponse | Log in a user Returns JWT token for subsequent API calls Optional tenant parameter for multi-tenant login |
| Logout | LogoutRequest | .google.protobuf.Empty | Log out a user Invalidates the current JWT token User can only logout their own session |
| IsLoggedIn | IsLoggedInRequest | IsLoggedInResponse | Check if a user is logged in Validates JWT token and checks if user session is still active |
| RefreshLoginWithTenant | RefreshLoginWithTenantRequest | LoginResponse | Refresh login with tenant context Transmutes a tenantless login session to a tenantful one Validates current JWT has no tenant, then issues new JWT with tenant_id SECURITY: Verifies user is associated with requested tenant before issuing new token |
| CreateApiKey | CreateApiKeyRequest | CreateApiKeyResponse | Creates an API key for the user, associated to the logged-in tenant API keys allow programmatic access without password authentication |
| GetApiKey | GetApiKeyRequest | GetApiKeyResponse | Gets the API key metadata for the user Returns key information but not the key value itself |
| DeleteApiKey | DeleteApiKeyRequest | .google.protobuf.Empty | Deletes the API key for the user Immediately invalidates the key |
| CreateServiceAccount | CreateServiceAccountRequest | CreateServiceAccountResponse | Creates a service account within a tenant Admin operation that provisions account + API key Returns API key secret (only shown once!) |
| ListServiceAccounts | ListServiceAccountsRequest | ListServiceAccountsResponse | Lists all service accounts in a tenant Admin operation |
| GetServiceAccount | GetServiceAccountRequest | ServiceAccount | Gets a specific service account by ID Admin operation |
| UpdateServiceAccount | UpdateServiceAccountRequest | .google.protobuf.Empty | Updates a service account (description, active status) Admin operation Cannot update username, email, or tenant_id |
| DeleteServiceAccount | DeleteServiceAccountRequest | .google.protobuf.Empty | Deletes a service account Admin operation Cascades to delete associated API keys |
| SetUserAttribute | SetUserAttributeRequest | .google.protobuf.Empty | Set attribute values for a user in the current tenant Adds the specified values to the user's attributes (upsert semantics) Existing values for the key are preserved; new values are added |
| RemoveUserAttribute | RemoveUserAttributeRequest | .google.protobuf.Empty | Remove attribute values from a user in the current tenant If attribute_values is empty, removes ALL values for the key If specific values provided, removes only those values |
| GetUserAttributes | GetUserAttributesRequest | GetUserAttributesResponse | Get all attributes for a user in the current tenant Returns attributes grouped by key with all values per key |
| ClearUserAttributes | ClearUserAttributesRequest | .google.protobuf.Empty | Clear all attributes for a user in the current tenant Removes every attribute key-value pair for the user |
| CheckAuthorization | CheckAuthorizationRequest | CheckAuthorizationResponse | Checks to see if the action is allowed Evaluates request against tenant's policy domains Request must contain "subject", "action", and "object" in context Object is a Resource that follows hc:// URI format |
| GetTenantAssociations | GetTenantAssociationsRequest | GetTenantAssociationsResponse | Get users associated with tenant Returns all users that have been granted access to the tenant |
| GetTenantUserAssociation | GetTenantUserAssociationRequest | GetTenantUserAssociationResponse | Get if tenant is associated for the specified user Returns true if user can act on behalf of the tenant |
| CreateTenantUserAssociation | CreateTenantUserAssociationRequest | .google.protobuf.Empty | Sets tenant to be associated for the specified user Grants user permission to act on behalf of the tenant |
| DeleteTenantUserAssociation | DeleteTenantUserAssociationRequest | .google.protobuf.Empty | Deletes tenant - user association Revokes user's permission to act on behalf of the tenant |
| CreateEmailVerificationToken | CreateEmailVerificationTokenRequest | CreateEmailVerificationTokenResponse | Creates an email verification token for the specified user Token is valid for the configured expiration period (default 24 hours) Automatically invalidates any existing unused tokens of the same type |
| ValidateEmailVerificationToken | ValidateEmailVerificationTokenRequest | ValidateEmailVerificationTokenResponse | Validates an email verification token Checks expiration, usage status, and token type Marks token as used upon successful validation |
| MarkEmailVerified | MarkEmailVerifiedRequest | .google.protobuf.Empty | Marks a user's email as verified Called after successful token validation during email verification flow |
| FindUserByOAuth | FindUserByOAuthRequest | FindUserByOAuthResponse | Finds a user by OAuth provider and subject ID Returns user information if found, error if not found |
| CreateOAuthUser | CreateOAuthUserRequest | CreateOAuthUserResponse | Creates a new user with OAuth credentials Associates user with specified tenant and marks email as verified |
| StoreOAuthState | StoreOAuthStateRequest | .google.protobuf.Empty | Stores OAuth state for CSRF protection during authorization flow State expires after configured period (default 10 minutes) |
| ValidateOAuthState | ValidateOAuthStateRequest | ValidateOAuthStateResponse | Validates OAuth state and retrieves associated information Checks expiration and marks state as used |
housecarl_lib/proto/healthz.proto
HealthCheckRequest
| Field | Type | Label | Description |
|---|---|---|---|
| service | string |
HealthCheckResponse
| Field | Type | Label | Description |
|---|---|---|---|
| status | HealthCheckResponse.ServingStatus |
HealthCheckResponse.ServingStatus
| Name | Number | Description |
|---|---|---|
| UNKNOWN | 0 | |
| SERVING | 1 | |
| NOT_SERVING | 2 | |
| SERVICE_UNKNOWN | 3 | Used only by the Watch method. |
Health
| Method Name | Request Type | Response Type | Description |
|---|---|---|---|
| Check | HealthCheckRequest | HealthCheckResponse | |
| Watch | HealthCheckRequest | HealthCheckResponse stream |
housecarl_lib/proto/jwt.proto
GetPublicKeyRequest
Request to get JWT public key
Empty request - the public key is the same for all callers
GetPublicKeyResponse
Response containing JWT public key information
| Field | Type | Label | Description |
|---|---|---|---|
| public_key_bytes | bytes | Ed25519 public key bytes | |
| algorithm | string | Algorithm type (e.g., "Ed25519") | |
| key_id | string | Optional key identifier for key rotation |
JwtService
JWT key management service for external service integration Provides public key distribution for JWT token verification
| Method Name | Request Type | Response Type | Description |
|---|---|---|---|
| GetPublicKey | GetPublicKeyRequest | GetPublicKeyResponse | Gets the public key for JWT verification External services use this to verify JWT tokens issued by Housecarl |