Skip to content

cli

import "runvoy/cmd/cli"

Package main implements the runvoy CLI tool. It provides commands for managing users and running remote commands.

Index

local

import "runvoy/cmd/local"

Package main implements the local development server for runvoy. It runs both the orchestrator and async event processor services locally for testing and development.

Index

api

import "runvoy/internal/api"

Package api defines the API types and structures used across runvoy.

Package api defines the API types and structures used across runvoy.

Package api defines the API types and structures used across runvoy.

Package api defines the API types and structures used across runvoy.

Package api defines the API types and structures used across runvoy. This file contains request and response structures for the secrets API.

Package api defines the API types and structures used across runvoy. It contains request and response structures for the orchestrator API.

Package api defines the API types and structures used across runvoy.

Package api defines the API types and structures used across runvoy.

Index

type AuthorizerHealthStatus

AuthorizerHealthStatus contains the health status for authorization data.

type AuthorizerHealthStatus struct {
    UsersWithInvalidRoles      []string `json:"users_with_invalid_roles"`
    UsersWithMissingRoles      []string `json:"users_with_missing_roles"`
    ResourcesWithMissingOwners []string `json:"resources_with_missing_owners"`
    OrphanedOwnerships         []string `json:"orphaned_ownerships"`
    MissingOwnerships          []string `json:"missing_ownerships"`
    TotalUsersChecked          int      `json:"total_users_checked"`
    TotalResourcesChecked      int      `json:"total_resources_checked"`
}

type ClaimAPIKeyResponse

ClaimAPIKeyResponse represents the response when claiming an API key

type ClaimAPIKeyResponse struct {
    APIKey    string `json:"api_key"`
    UserEmail string `json:"user_email"`
    Message   string `json:"message,omitempty"`
}

type ComputeHealthStatus

ComputeHealthStatus contains the health status for compute resources (e.g., containers, task definitions).

type ComputeHealthStatus struct {
    TotalResources    int      `json:"total_resources"`
    VerifiedCount     int      `json:"verified_count"`
    RecreatedCount    int      `json:"recreated_count"`
    TagUpdatedCount   int      `json:"tag_updated_count"`
    OrphanedCount     int      `json:"orphaned_count"`
    OrphanedResources []string `json:"orphaned_resources"`
}

type CreateSecretRequest

CreateSecretRequest represents the request to create a new secret

type CreateSecretRequest struct {
    Name        string `json:"name"`     // Internal identifier for the secret
    KeyName     string `json:"key_name"` // Environment variable name (e.g., GITHUB_TOKEN)
    Description string `json:"description,omitempty"`
    Value       string `json:"value"` // The secret value to store
}

type CreateSecretResponse

CreateSecretResponse represents the response after creating a secret To avoid exposing secret data, only a success message is returned.

type CreateSecretResponse struct {
    Message string `json:"message"`
}

type CreateUserRequest

CreateUserRequest represents the request to create a new user

type CreateUserRequest struct {
    Email  string `json:"email"`
    APIKey string `json:"api_key,omitempty"` // Optional: if not provided, one will be generated
    Role   string `json:"role"`              // Required: admin, operator, developer, or viewer
}

type CreateUserResponse

CreateUserResponse represents the response after creating a user

type CreateUserResponse struct {
    User       *User  `json:"user"`
    ClaimToken string `json:"claim_token"`
}

type DeleteSecretRequest

DeleteSecretRequest represents the request to delete a secret The secret name is provided in the URL path parameter

type DeleteSecretRequest struct {
    Name string `json:"name"` // Secret name from URL path
}

type DeleteSecretResponse

DeleteSecretResponse represents the response after deleting a secret

type DeleteSecretResponse struct {
    Name    string `json:"name"`
    Message string `json:"message"`
}

type ErrorResponse

ErrorResponse represents an error response

type ErrorResponse struct {
    Error   string `json:"error"`
    Code    string `json:"code,omitempty"`
    Details string `json:"details,omitempty"`
}

type Execution

Execution represents an execution record

type Execution struct {
    ExecutionID         string     `json:"execution_id"`
    CreatedBy           string     `json:"created_by"`
    OwnedBy             []string   `json:"owned_by"`
    Command             string     `json:"command"`
    StartedAt           time.Time  `json:"started_at"`
    CompletedAt         *time.Time `json:"completed_at,omitempty"`
    Status              string     `json:"status"`
    ExitCode            int        `json:"exit_code"`
    DurationSeconds     int        `json:"duration_seconds,omitempty"`
    LogStreamName       string     `json:"log_stream_name,omitempty"`
    CreatedByRequestID  string     `json:"created_by_request_id"`
    ModifiedByRequestID string     `json:"modified_by_request_id"`
    ComputePlatform     string     `json:"cloud,omitempty"`
}

type ExecutionRequest

ExecutionRequest represents a request to execute a command

type ExecutionRequest struct {
    Command string            `json:"command"`
    Image   string            `json:"image,omitempty"`
    Env     map[string]string `json:"env,omitempty"`
    Timeout int               `json:"timeout,omitempty"`
    Secrets []string          `json:"secrets,omitempty"`

    // Git repository configuration (optional sidecar pattern)
    GitRepo string `json:"git_repo,omitempty"` // Git repository URL (e.g., "https://github.com/user/repo.git")
    GitRef  string `json:"git_ref,omitempty"`  // Git branch, tag, or commit SHA (default: "main")
    GitPath string `json:"git_path,omitempty"` // Working directory within the cloned repo (default: ".")

    // SecretVarNames contains the environment variable names that should be treated as secrets.
    // This is populated by the service layer after resolving secrets from the Secrets field.
    // It includes both explicitly resolved secrets and pattern-detected sensitive variables.
    SecretVarNames []string `json:"-"` // Not serialized in API responses
}

type ExecutionResponse

ExecutionResponse represents the response to an execution request

type ExecutionResponse struct {
    ExecutionID string `json:"execution_id"`
    LogURL      string `json:"log_url"`
    Status      string `json:"status"`
    ImageID     string `json:"image_id"`
}

type ExecutionStatusResponse

ExecutionStatusResponse represents the current status of an execution

type ExecutionStatusResponse struct {
    ExecutionID string     `json:"execution_id"`
    Status      string     `json:"status"`
    StartedAt   time.Time  `json:"started_at"`
    ExitCode    *int       `json:"exit_code"`
    CompletedAt *time.Time `json:"completed_at,omitempty"`
}

type GetSecretRequest

GetSecretRequest represents the request to retrieve a secret The secret name is provided in the URL path parameter

type GetSecretRequest struct {
    Name string `json:"name"` // Secret name from URL path
}

type GetSecretResponse

GetSecretResponse represents the response when retrieving a secret

type GetSecretResponse struct {
    Secret *Secret `json:"secret"`
}

type HealthIssue

HealthIssue represents a single health issue found during reconciliation.

type HealthIssue struct {
    // ResourceType is provider-specific resource type (e.g., "ecs_task_definition", "cloud_run_service")
    ResourceType string `json:"resource_type"`
    ResourceID   string `json:"resource_id"`
    Severity     string `json:"severity"` // "error", "warning"
    Message      string `json:"message"`
    Action       string `json:"action"` // "recreated", "requires_manual_intervention", "reported", "tag_updated"
}

type HealthReconcileResponse

HealthReconcileResponse is returned by POST /api/v1/health/reconcile.

type HealthReconcileResponse struct {
    Status string        `json:"status"`
    Report *HealthReport `json:"report"`
}

type HealthReport

HealthReport contains the results of a health reconciliation run.

type HealthReport struct {
    Timestamp        time.Time              `json:"timestamp"`
    ComputeStatus    ComputeHealthStatus    `json:"compute_status"`
    SecretsStatus    SecretsHealthStatus    `json:"secrets_status"`
    IdentityStatus   IdentityHealthStatus   `json:"identity_status"`
    AuthorizerStatus AuthorizerHealthStatus `json:"authorizer_status"`
    Issues           []HealthIssue          `json:"issues"`
    ReconciledCount  int                    `json:"reconciled_count"`
    ErrorCount       int                    `json:"error_count"`
}

type HealthResponse

HealthResponse represents the response to a health check request

type HealthResponse struct {
    Status  string `json:"status"`
    Version string `json:"version"`
}

type IdentityHealthStatus

IdentityHealthStatus contains the health status for identity and access management resources.

type IdentityHealthStatus struct {
    DefaultRolesVerified bool     `json:"default_roles_verified"`
    CustomRolesVerified  int      `json:"custom_roles_verified"`
    CustomRolesTotal     int      `json:"custom_roles_total"`
    MissingRoles         []string `json:"missing_roles"`
}

type ImageInfo

ImageInfo represents information about a registered image

type ImageInfo struct {
    ImageID               string    `json:"image_id"`
    Image                 string    `json:"image"`
    TaskDefinitionName    string    `json:"task_definition_name,omitempty"`
    IsDefault             *bool     `json:"is_default,omitempty"`
    TaskRoleName          *string   `json:"task_role_name,omitempty"`
    TaskExecutionRoleName *string   `json:"task_execution_role_name,omitempty"`
    CPU                   int       `json:"cpu,omitempty"`
    Memory                int       `json:"memory,omitempty"`
    RuntimePlatform       string    `json:"runtime_platform,omitempty"`
    ImageRegistry         string    `json:"image_registry,omitempty"`
    ImageName             string    `json:"image_name,omitempty"`
    ImageTag              string    `json:"image_tag,omitempty"`
    CreatedBy             string    `json:"created_by,omitempty"`
    OwnedBy               []string  `json:"owned_by"`
    CreatedAt             time.Time `json:"created_at"`
    CreatedByRequestID    string    `json:"created_by_request_id"`
    ModifiedByRequestID   string    `json:"modified_by_request_id"`
}

type KillExecutionResponse

KillExecutionResponse represents the response after killing an execution

type KillExecutionResponse struct {
    ExecutionID string `json:"execution_id"`
    Message     string `json:"message"`
}

type ListImagesResponse

ListImagesResponse represents the response containing all registered images

type ListImagesResponse struct {
    Images []ImageInfo `json:"images"`
}

type ListSecretsRequest

ListSecretsRequest represents the request to list secrets Optionally filters by user email

type ListSecretsRequest struct {
    CreatedBy string `json:"created_by,omitempty"` // Filter by user who created the secret
}

type ListSecretsResponse

ListSecretsResponse represents the response containing all secrets

type ListSecretsResponse struct {
    Secrets []*Secret `json:"secrets"`
    Total   int       `json:"total"`
}

type ListUsersResponse

ListUsersResponse represents the response containing all users

type ListUsersResponse struct {
    Users []*User `json:"users"`
}

type LogEvent

LogEvent represents a single log event. Events are ordered by timestamp. Clients should sort by timestamp and compute line numbers as needed for display purposes.

type LogEvent struct {
    Timestamp int64  `json:"timestamp"` // Unix timestamp in milliseconds
    Message   string `json:"message"`   // The actual log message text
}

type LogsResponse

LogsResponse contains all log events for an execution

type LogsResponse struct {
    ExecutionID string     `json:"execution_id"`
    Events      []LogEvent `json:"events"`

    // Current execution status (RUNNING, SUCCEEDED, FAILED, STOPPED)
    Status string `json:"status"`

    // WebSocket URL for streaming logs (only provided if execution is RUNNING)
    WebSocketURL string `json:"websocket_url,omitempty"`
}

type PendingAPIKey

PendingAPIKey represents a pending API key awaiting claim

type PendingAPIKey struct {
    SecretToken  string     `json:"secret_token"`
    APIKey       string     `json:"api_key"`
    UserEmail    string     `json:"user_email"`
    CreatedBy    string     `json:"created_by"`
    CreatedAt    time.Time  `json:"created_at"`
    ExpiresAt    int64      `json:"expires_at"` // Unix timestamp for TTL
    Viewed       bool       `json:"viewed"`
    ViewedAt     *time.Time `json:"viewed_at,omitempty"`
    ViewedFromIP string     `json:"viewed_from_ip,omitempty"`
}

type Playbook

Playbook represents a reusable command execution configuration

type Playbook struct {
    Description string            `yaml:"description,omitempty"`
    Image       string            `yaml:"image,omitempty"`
    GitRepo     string            `yaml:"git_repo,omitempty"`
    GitRef      string            `yaml:"git_ref,omitempty"`
    GitPath     string            `yaml:"git_path,omitempty"`
    Secrets     []string          `yaml:"secrets,omitempty"`
    Env         map[string]string `yaml:"env,omitempty"`
    Commands    []string          `yaml:"commands"`
}

type RegisterImageRequest

RegisterImageRequest represents the request to register a new Docker image

type RegisterImageRequest struct {
    Image                 string  `json:"image"`
    IsDefault             *bool   `json:"is_default,omitempty"`
    TaskRoleName          *string `json:"task_role_name,omitempty"`
    TaskExecutionRoleName *string `json:"task_execution_role_name,omitempty"`
    CPU                   *int    `json:"cpu,omitempty"`
    Memory                *int    `json:"memory,omitempty"`
    RuntimePlatform       *string `json:"runtime_platform,omitempty"`
}

type RegisterImageResponse

RegisterImageResponse represents the response after registering an image

type RegisterImageResponse struct {
    Image   string `json:"image"`
    Message string `json:"message"`
}

type RelatedResources

RelatedResources contains all resources associated with a request ID

type RelatedResources struct {
    Executions []*Execution `json:"executions,omitempty"`
    Secrets    []*Secret    `json:"secrets,omitempty"`
    Users      []*User      `json:"users,omitempty"`
    Images     []ImageInfo  `json:"images,omitempty"`
}

type RemoveImageRequest

RemoveImageRequest represents the request to remove a Docker image

type RemoveImageRequest struct {
    Image string `json:"image"`
}

type RemoveImageResponse

RemoveImageResponse represents the response after removing an image

type RemoveImageResponse struct {
    Image   string `json:"image"`
    Message string `json:"message"`
}

type RevokeUserRequest

RevokeUserRequest represents the request to revoke a user's API key

type RevokeUserRequest struct {
    Email string `json:"email"`
}

type RevokeUserResponse

RevokeUserResponse represents the response after revoking a user

type RevokeUserResponse struct {
    Message string `json:"message"`
    Email   string `json:"email"`
}

type Secret

Secret represents a secret with its metadata and optionally its value

type Secret struct {
    Name                string    `json:"name"`     // Internal identifier for the secret
    KeyName             string    `json:"key_name"` // Environment variable name (e.g., GITHUB_TOKEN)
    Description         string    `json:"description,omitempty"`
    Value               string    `json:"value,omitempty"`
    CreatedBy           string    `json:"created_by"`
    OwnedBy             []string  `json:"owned_by"`
    CreatedAt           time.Time `json:"created_at"`
    UpdatedAt           time.Time `json:"updated_at"`
    UpdatedBy           string    `json:"updated_by"`
    CreatedByRequestID  string    `json:"created_by_request_id"`
    ModifiedByRequestID string    `json:"modified_by_request_id"`
}

type SecretsHealthStatus

SecretsHealthStatus contains the health status for secrets/parameters.

type SecretsHealthStatus struct {
    TotalSecrets       int      `json:"total_secrets"`
    VerifiedCount      int      `json:"verified_count"`
    TagUpdatedCount    int      `json:"tag_updated_count"`
    MissingCount       int      `json:"missing_count"`
    OrphanedCount      int      `json:"orphaned_count"`
    OrphanedParameters []string `json:"orphaned_parameters"`
}

type TraceResponse

TraceResponse contains logs and related resources for a request ID

type TraceResponse struct {
    // Logs retrieved from backend infrastructure
    Logs []LogEvent `json:"logs"`

    // Related resources associated with this request ID
    RelatedResources RelatedResources `json:"related_resources"`
}

type UpdateSecretRequest

UpdateSecretRequest represents the request to update a secret (metadata and/or value) Users can update: description, key_name (environment variable name), and value. Description and KeyName are metadata fields. UpdatedAt is always refreshed. If Value is provided, the secret's value will be updated.

type UpdateSecretRequest struct {
    Description string `json:"description,omitempty"` // Environment variable description
    KeyName     string `json:"key_name,omitempty"`    // Environment variable name (e.g., GITHUB_TOKEN)
    Value       string `json:"value,omitempty"`       // If provided, updates the secret value
}

type UpdateSecretResponse

UpdateSecretResponse represents the response after updating a secret To avoid exposing secret data, only a success message is returned.

type UpdateSecretResponse struct {
    Message string `json:"message"`
}

type User

User represents a user in the system

type User struct {
    Email               string     `json:"email"`
    APIKey              string     `json:"api_key,omitempty"`
    Role                string     `json:"role"`
    CreatedAt           time.Time  `json:"created_at"`
    Revoked             bool       `json:"revoked"`
    LastUsed            *time.Time `json:"last_used,omitempty"`
    CreatedByRequestID  string     `json:"created_by_request_id"`
    ModifiedByRequestID string     `json:"modified_by_request_id"`
}

type WebSocketConnection

WebSocketConnection represents a WebSocket connection record

type WebSocketConnection struct {
    ConnectionID  string `json:"connection_id"`
    ExecutionID   string `json:"execution_id"`
    Functionality string `json:"functionality"`
    ExpiresAt     int64  `json:"expires_at"`
    ClientIP      string `json:"client_ip,omitempty"`
    Token         string `json:"token,omitempty"`
    UserEmail     string `json:"user_email,omitempty"`
    // Client IP captured when the websocket token was created (for tracing)
    TokenRequestClientIP string `json:"token_request_client_ip,omitempty"`
}

type WebSocketDisconnectReason

WebSocketDisconnectReason represents the reason for a disconnect

type WebSocketDisconnectReason string
const (
    // WebSocketDisconnectReasonExecutionCompleted indicates the execution has completed
    WebSocketDisconnectReasonExecutionCompleted WebSocketDisconnectReason = "execution_completed"
)

type WebSocketMessage

WebSocketMessage represents a WebSocket message sent to clients

type WebSocketMessage struct {
    Type      WebSocketMessageType       `json:"type"`
    Reason    *WebSocketDisconnectReason `json:"reason,omitempty"`
    Message   *string                    `json:"message,omitempty"`
    Timestamp *int64                     `json:"timestamp,omitempty"`
}

type WebSocketMessageType

WebSocketMessageType represents the type of WebSocket message

type WebSocketMessageType string
const (
    // WebSocketMessageTypeLog represents a log event message
    WebSocketMessageTypeLog WebSocketMessageType = "log"
    // WebSocketMessageTypeDisconnect represents a disconnect notification message
    WebSocketMessageTypeDisconnect WebSocketMessageType = "disconnect"
)

type WebSocketToken

WebSocketToken represents a WebSocket authentication token

type WebSocketToken struct {
    Token       string `json:"token"`
    ExecutionID string `json:"execution_id"`
    UserEmail   string `json:"user_email,omitempty"`
    // Client IP captured when the websocket token was created (for tracing)
    ClientIP  string `json:"client_ip,omitempty"`
    ExpiresAt int64  `json:"expires_at"`
    CreatedAt int64  `json:"created_at"`
}

auth

import "runvoy/internal/auth"

Package auth provides authentication utilities for runvoy.

Index

func GenerateSecretToken

func GenerateSecretToken() (string, error)

GenerateSecretToken creates a cryptographically secure random secret token. Used for claim URLs, WebSocket authentication, and other temporary access tokens. The token is base64-encoded and approximately 32 characters long.

func GenerateUUID

func GenerateUUID() string

GenerateUUID generates a UUID-like identifier using crypto/rand. Returns a hex-encoded string of 32 characters (16 random bytes). Used for generating unique identifiers such as task definition family names.

func HashAPIKey

func HashAPIKey(apiKey string) string

HashAPIKey creates a SHA-256 hash of the API key for secure storage. NOTICE: we never store plain API keys in the database.

client

import "runvoy/internal/client"

Package client provides HTTP client functionality for the runvoy API. It handles authentication, request/response serialization, and error handling.

Package client provides HTTP client functionality for the runvoy API.

Index

type Client

Client provides a generic HTTP client for API operations

type Client struct {
    // contains filtered or unexported fields
}

func New

func New(cfg *config.Config, log *slog.Logger) *Client

New creates a new API client

func (*Client) ClaimAPIKey

func (c *Client) ClaimAPIKey(ctx context.Context, token string) (*api.ClaimAPIKeyResponse, error)

ClaimAPIKey claims a user's API key

func (*Client) CreateSecret

func (c *Client) CreateSecret(ctx context.Context, req api.CreateSecretRequest) (*api.CreateSecretResponse, error)

CreateSecret creates a new secret

func (*Client) CreateUser

func (c *Client) CreateUser(ctx context.Context, req api.CreateUserRequest) (*api.CreateUserResponse, error)

CreateUser creates a new user using the API

func (*Client) DeleteSecret

func (c *Client) DeleteSecret(ctx context.Context, name string) (*api.DeleteSecretResponse, error)

DeleteSecret deletes a secret by name

func (*Client) Do

func (c *Client) Do(ctx context.Context, req Request) (*Response, error)

Do makes an HTTP request to the API

func (*Client) DoJSON

func (c *Client) DoJSON(ctx context.Context, req Request, result any) error

DoJSON makes a request and unmarshals the response into the provided interface

func (*Client) FetchBackendLogs

func (c *Client) FetchBackendLogs(ctx context.Context, requestID string) (*api.TraceResponse, error)

FetchBackendLogs fetches backend infrastructure logs and related resources for a request ID

func (*Client) GetExecutionStatus

func (c *Client) GetExecutionStatus(ctx context.Context, executionID string) (*api.ExecutionStatusResponse, error)

GetExecutionStatus gets the status of an execution

func (*Client) GetHealth

func (c *Client) GetHealth(ctx context.Context) (*api.HealthResponse, error)

GetHealth checks the API health status

func (*Client) GetImage

func (c *Client) GetImage(ctx context.Context, image string) (*api.ImageInfo, error)

GetImage retrieves a single container image by ID or name

func (*Client) GetLogs

func (c *Client) GetLogs(ctx context.Context, executionID string) (*api.LogsResponse, error)

GetLogs gets the logs for an execution The response includes a WebSocketURL field for streaming logs if WebSocket is configured

func (*Client) GetSecret

func (c *Client) GetSecret(ctx context.Context, name string) (*api.GetSecretResponse, error)

GetSecret retrieves a secret by name

func (*Client) KillExecution

func (c *Client) KillExecution(ctx context.Context, executionID string) (*api.KillExecutionResponse, error)

KillExecution stops a running execution by its ID Returns nil response if the execution was already terminated (204 No Content)

func (*Client) ListExecutions

func (c *Client) ListExecutions(ctx context.Context, limit int, statuses string) ([]api.Execution, error)

ListExecutions fetches executions with optional filtering and pagination. Parameters:

  • limit: maximum number of executions to return (0 returns all)
  • statuses: comma-separated list of execution statuses to filter by (e.g., "RUNNING,TERMINATING")

func (*Client) ListImages

func (c *Client) ListImages(ctx context.Context) (*api.ListImagesResponse, error)

ListImages retrieves all registered container images

func (*Client) ListSecrets

func (c *Client) ListSecrets(ctx context.Context) (*api.ListSecretsResponse, error)

ListSecrets lists all secrets

func (*Client) ListUsers

func (c *Client) ListUsers(ctx context.Context) (*api.ListUsersResponse, error)

ListUsers lists all users

func (*Client) ReconcileHealth

func (c *Client) ReconcileHealth(ctx context.Context) (*api.HealthReconcileResponse, error)

ReconcileHealth triggers a full health reconciliation on the server. Requires authentication and returns a reconciliation report.

func (*Client) RegisterImage

func (c *Client) RegisterImage(ctx context.Context, image string, isDefault *bool, taskRoleName, taskExecutionRoleName *string, cpu, memory *int, runtimePlatform *string) (*api.RegisterImageResponse, error)

RegisterImage registers a new container image for execution, optionally marking it as the default

func (*Client) RevokeUser

func (c *Client) RevokeUser(ctx context.Context, req api.RevokeUserRequest) (*api.RevokeUserResponse, error)

RevokeUser revokes a user's API key

func (*Client) RunCommand

func (c *Client) RunCommand(ctx context.Context, req *api.ExecutionRequest) (*api.ExecutionResponse, error)

RunCommand executes a command remotely via the runvoy API.

func (*Client) UnregisterImage

func (c *Client) UnregisterImage(ctx context.Context, image string) (*api.RemoveImageResponse, error)

UnregisterImage removes a container image from the registry

func (*Client) UpdateSecret

func (c *Client) UpdateSecret(ctx context.Context, name string, req api.UpdateSecretRequest) (*api.UpdateSecretResponse, error)

UpdateSecret updates a secret by name

type Interface

Interface defines the API client interface for dependency injection and testing

type Interface interface {
    // Health
    ReconcileHealth(ctx context.Context) (*api.HealthReconcileResponse, error)
    GetLogs(ctx context.Context, executionID string) (*api.LogsResponse, error)
    FetchBackendLogs(ctx context.Context, requestID string) (*api.TraceResponse, error)
    GetExecutionStatus(ctx context.Context, executionID string) (*api.ExecutionStatusResponse, error)
    RunCommand(ctx context.Context, req *api.ExecutionRequest) (*api.ExecutionResponse, error)
    KillExecution(ctx context.Context, executionID string) (*api.KillExecutionResponse, error)
    ListExecutions(ctx context.Context, limit int, statuses string) ([]api.Execution, error)
    ClaimAPIKey(ctx context.Context, token string) (*api.ClaimAPIKeyResponse, error)
    CreateUser(ctx context.Context, req api.CreateUserRequest) (*api.CreateUserResponse, error)
    RevokeUser(ctx context.Context, req api.RevokeUserRequest) (*api.RevokeUserResponse, error)
    ListUsers(ctx context.Context) (*api.ListUsersResponse, error)
    RegisterImage(
        ctx context.Context,
        image string,
        isDefault *bool,
        taskRoleName, taskExecutionRoleName *string,
        cpu, memory *int,
        runtimePlatform *string,
    ) (*api.RegisterImageResponse, error)
    ListImages(ctx context.Context) (*api.ListImagesResponse, error)
    GetImage(ctx context.Context, image string) (*api.ImageInfo, error)
    UnregisterImage(ctx context.Context, image string) (*api.RemoveImageResponse, error)
    CreateSecret(ctx context.Context, req api.CreateSecretRequest) (*api.CreateSecretResponse, error)
    GetSecret(ctx context.Context, name string) (*api.GetSecretResponse, error)
    ListSecrets(ctx context.Context) (*api.ListSecretsResponse, error)
    UpdateSecret(ctx context.Context, name string, req api.UpdateSecretRequest) (*api.UpdateSecretResponse, error)
    DeleteSecret(ctx context.Context, name string) (*api.DeleteSecretResponse, error)
}

type Request

Request represents an API request

type Request struct {
    Method string
    Path   string
    Body   any
}

type Response

Response represents an API response

type Response struct {
    StatusCode int
    Body       []byte
}

config

import "runvoy/internal/config"

Package config manages configuration for the runvoy CLI and services. It uses Viper for unified configuration management from files and environment variables.

Index

func GetConfigPath

func GetConfigPath() (string, error)

GetConfigPath returns the path to the config file

func Save

func Save(config *Config) error

Save saves the configuration to the user's home directory. Overwrites the existing config file if it exists.

type Config

Config represents the unified configuration structure for both CLI and services. It supports loading from YAML files and environment variables. Provider-specific configurations are nested under their respective provider keys.

type Config struct {
    // CLI Configuration
    APIEndpoint string `mapstructure:"api_endpoint" yaml:"api_endpoint" validate:"omitempty,url"`
    APIKey      string `mapstructure:"api_key" yaml:"api_key"`
    WebURL      string `mapstructure:"web_url" yaml:"web_url" validate:"omitempty,url"`

    // Backend Service Configuration
    BackendProvider    constants.BackendProvider `mapstructure:"backend_provider" yaml:"backend_provider"`
    InitTimeout        time.Duration             `mapstructure:"init_timeout"`
    LogLevel           string                    `mapstructure:"log_level"`
    Port               int                       `mapstructure:"port" validate:"omitempty"`
    RequestTimeout     time.Duration             `mapstructure:"request_timeout"`
    CORSAllowedOrigins []string                  `mapstructure:"cors_allowed_origins" yaml:"cors_allowed_origins"`

    // Provider-specific configurations
    AWS *awsconfig.Config `mapstructure:"aws" yaml:"aws,omitempty"`
}

func Load

func Load() (*Config, error)

Load loads the configuration using Viper. For CLI: loads from \~/.runvoy/config.yaml For services: loads from environment variables with RUNVOY_ prefix Environment variables take precedence over config file values.

func LoadCLI

func LoadCLI() (*Config, error)

LoadCLI loads configuration specifically for CLI usage. Returns an error if the config file doesn't exist.

func LoadEventProcessor

func LoadEventProcessor() (*Config, error)

LoadEventProcessor loads configuration for the event processor service. Loads from environment variables and validates required fields.

func LoadOrchestrator

func LoadOrchestrator() (*Config, error)

LoadOrchestrator loads configuration for the orchestrator service. Loads from environment variables and validates required fields. This maintains parity with the Lambda orchestrator which requires all AWS resources.

func MustLoadEventProcessor

func MustLoadEventProcessor() *Config

MustLoadEventProcessor loads event processor configuration and exits on error. Suitable for application startup where configuration errors should be fatal.

func MustLoadOrchestrator

func MustLoadOrchestrator() *Config

MustLoadOrchestrator loads orchestrator configuration and exits on error. Suitable for application startup where configuration errors should be fatal.

func (*Config) GetDefaultStackName

func (c *Config) GetDefaultStackName() string

GetDefaultStackName returns the default infrastructure stack name. Returns the configured value or the default if not set.

func (*Config) GetLogLevel

func (c *Config) GetLogLevel() slog.Level

GetLogLevel returns the slog.Level from the string configuration. Defaults to INFO if the level string is invalid.

func (*Config) GetProviderIdentifier

func (c *Config) GetProviderIdentifier() string

GetProviderIdentifier returns the lowercase provider identifier string. For AWS, returns "aws".

constants

import "runvoy/internal/constants"

Package constants defines global constants used throughout runvoy.

Package constants defines global constants used throughout runvoy.

Package constants defines global constants used throughout runvoy.

Package constants defines global constants used throughout runvoy.

Package constants defines global constants used throughout runvoy.

Package constants defines global constants used throughout runvoy.

Package constants defines global constants used throughout runvoy.

Package constants defines global constants used throughout runvoy.

Package constants defines global constants used throughout runvoy.

Package constants defines global constants used throughout runvoy.

Package constants defines global constants used throughout runvoy.

Package constants defines global constants used throughout runvoy.

Package constants defines global constants used throughout runvoy.

Package constants defines global constants used throughout runvoy.

Index

Constants

APIKeyByteSize is the number of random bytes used to generate API keys

const APIKeyByteSize = 24

APIKeyHeader is the HTTP header name for API key authentication

const APIKeyHeader = "X-API-Key"

BoxBorderPadding is the padding used in box borders

const BoxBorderPadding = 2

ClaimURLExpirationMinutes is the number of minutes after which a claim URL expires

const ClaimURLExpirationMinutes = 15

ConfigDirName is the name of the configuration directory in the user's home directory

const ConfigDirName = "." + ProjectName

ConfigDirPermissions is the file system permissions for config directory (0750)

const ConfigDirPermissions = 0750

ConfigFileName is the name of the global configuration file

const ConfigFileName = "config.yaml"

ConfigFilePermissions is the file system permissions for config file (0600)

const ConfigFilePermissions = 0600

ConnectionTTLHours is the time-to-live for connection records in the database (24 hours)

const ConnectionTTLHours = 24

ContentTypeHeader is the HTTP Content-Type header name.

const ContentTypeHeader = "Content-Type"

DefaultContextTimeout is the default timeout for context operations

const DefaultContextTimeout = 10 * time.Second

DefaultGitRef is the default Git reference to use if no reference is provided

const DefaultGitRef = "main"

DefaultWebURL is the default URL of the web application HTML file. This can be overridden via configuration (RUNVOY_WEB_URL env var or config file).

const DefaultWebURL = "https://runvoy.site/"

EnvVarSplitLimit is the limit for splitting environment variable strings (KEY=VALUE)

const EnvVarSplitLimit = 2

ExecutionsSliceInitialCapacity is the initial capacity for executions slices

const ExecutionsSliceInitialCapacity = 64

ExpectedArgsCreateConfigFile is the expected number of arguments for create-config-file script

const ExpectedArgsCreateConfigFile = 2

ExpectedArgsSeedAdminUser is the expected number of arguments for seed-admin-user script

const ExpectedArgsSeedAdminUser = 3

ExpectedArgsTruncateDynamoDBTable is the expected number of arguments for truncate-dynamodb-table script

const ExpectedArgsTruncateDynamoDBTable = 2

FunctionalityLogStreaming identifies connections used for streaming execution logs

const FunctionalityLogStreaming = "log_streaming"

HTTPStatusBadRequest is the HTTP status code for bad requests (400)

const HTTPStatusBadRequest = 400

HTTPStatusServerError is the HTTP status code for server errors (500)

const HTTPStatusServerError = 500

HeaderSeparatorLength is the length of the header separator line

const HeaderSeparatorLength = 50

LocalDevelopmentURL is the default URL of the local development server.

const LocalDevelopmentURL = "http://localhost:5173/"

LongScriptContextTimeout is the timeout for longer script context operations

const LongScriptContextTimeout = 30 * time.Second

MaxConcurrentSends is the maximum number of concurrent sends to WebSocket connections

const MaxConcurrentSends = 10

MillisecondsPerSecond is the number of milliseconds in a second

const MillisecondsPerSecond = 1000

MinimumArgsDeleteS3Buckets is the minimum number of arguments for delete-s3-buckets script (script name + at least 1 bucket)

const MinimumArgsDeleteS3Buckets = 2

MinimumArgsUpdateReadmeHelp is the minimum number of arguments for update-readme-help script

const MinimumArgsUpdateReadmeHelp = 2

MinutesPerHour is the number of minutes in an hour

const MinutesPerHour = 60

PercentageMultiplier is the multiplier to convert fraction to percentage

const PercentageMultiplier = 100

PlaybookDirName is the name of the playbook directory in the current working directory

const PlaybookDirName = ".runvoy"

ProgressBarWidth is the default width for progress bars

const ProgressBarWidth = 40

ProjectName is the name of the CLI tool and application

const ProjectName = "runvoy"

RegexMatchCountEnvVar is the expected number of regex matches for environment variable parsing

const RegexMatchCountEnvVar = 3

RequestIDLogField is the field name used for request ID in log entries

const RequestIDLogField = "request_id"

ResourceApplicationTagKey is the tag key for Application. This tag is used to identify the application that manages a resource.

const ResourceApplicationTagKey = "Application"

ResourceManagedByTagKey is the tag key for ManagedBy. This tag is used to identify what system or tool manages a resource.

const ResourceManagedByTagKey = "ManagedBy"

ScriptContextTimeout is the timeout for script context operations

const ScriptContextTimeout = 10 * time.Second

SecondsPerMinute is the number of seconds in a minute

const SecondsPerMinute = 60

SecretTokenByteSize is the number of random bytes used to generate secret tokens

const SecretTokenByteSize = 24

ServerIdleTimeout is the HTTP server idle timeout

const ServerIdleTimeout = 60 * time.Second

ServerReadTimeout is the HTTP server read timeout

const ServerReadTimeout = 15 * time.Second

ServerShutdownTimeout is the timeout for graceful server shutdown

const ServerShutdownTimeout = 5 * time.Second

ServerWriteTimeout is the HTTP server write timeout

const ServerWriteTimeout = 15 * time.Second

SpinnerTickerInterval is the interval between spinner frame updates

const SpinnerTickerInterval = 80 * time.Millisecond

TestContextTimeout is the timeout for test contexts

const TestContextTimeout = 5 * time.Second

UUIDByteSize is the number of random bytes used to generate UUIDs 16 bytes = 128 bits, same as a UUID

const UUIDByteSize = 16

Variables

DefaultCORSAllowedOrigins is the default list of allowed CORS origins. Defaults to the web application URL and local development URL.

var DefaultCORSAllowedOrigins = []string{
    DefaultWebURL,
    LocalDevelopmentURL,
}

PlaybookFileExtensions are the valid file extensions for playbook files

var PlaybookFileExtensions = []string{".yaml", ".yml"}

func CanTransition

func CanTransition(from, to ExecutionStatus) bool

CanTransition checks if a status transition from 'from' to 'to' is valid. Returns true if the transition is allowed, false otherwise. If the source status is not in the validTransitions map, returns false.

func ConfigDirPath

func ConfigDirPath(homeDir string) string

ConfigDirPath returns the full path to the global configuration directory.

func ConfigFilePath

func ConfigFilePath(homeDir string) string

ConfigFilePath returns the full path to the global configuration file

func GetVersion

func GetVersion() *string

GetVersion returns the current version of runvoy.

type BackendProvider

BackendProvider represents the backend infrastructure provider.

type BackendProvider string
const (
    // AWS is the Amazon Web Services backend provider.
    AWS BackendProvider = "AWS"
)

type ConfigCtxKeyType

ConfigCtxKeyType is the type for the config context key

type ConfigCtxKeyType string

ConfigCtxKey is the key used to store config in context

const ConfigCtxKey ConfigCtxKeyType = "config"

type Environment

Environment represents the execution environment (e.g., CLI, Lambda).

type Environment string

Environment types for logger configuration

const (
    Development Environment = "development"
    Production  Environment = "production"
    CLI         Environment = "cli"
)

type ExecutionStatus

ExecutionStatus represents the business-level status of a command execution. This is distinct from EcsStatus, which reflects the AWS ECS task lifecycle. Execution statuses are used throughout the API and stored in the database.

type ExecutionStatus string
const (
    // ExecutionStarting indicates the command has been accepted and is being scheduled
    ExecutionStarting ExecutionStatus = "STARTING"
    // ExecutionRunning indicates the command is currently executing
    ExecutionRunning ExecutionStatus = "RUNNING"
    // ExecutionSucceeded indicates the command completed successfully
    ExecutionSucceeded ExecutionStatus = "SUCCEEDED"
    // ExecutionFailed indicates the command failed with an error
    ExecutionFailed ExecutionStatus = "FAILED"
    // ExecutionStopped indicates the command was manually terminated
    ExecutionStopped ExecutionStatus = "STOPPED"
    // ExecutionTerminating indicates a stop request is in progress
    ExecutionTerminating ExecutionStatus = "TERMINATING"

    // DefaultExecutionListLimit is the default number of executions returned by the list endpoint
    DefaultExecutionListLimit = 10
)

func TerminalExecutionStatuses

func TerminalExecutionStatuses() []ExecutionStatus

TerminalExecutionStatuses returns all statuses that represent completed executions

type Service

Service represents a runvoy service component.

type Service string
const (
    // OrchestratorService is the main orchestrator service.
    OrchestratorService Service = "orchestrator"
    // EventProcessorService is the event processing service.
    EventProcessorService Service = "event-processor"
)

type StartTimeCtxKeyType

StartTimeCtxKeyType is the type for start time context keys

type StartTimeCtxKeyType string

StartTimeCtxKey is the key used to store the start time in context

const StartTimeCtxKey StartTimeCtxKeyType = "startTime"

database

import "runvoy/internal/database"

Package database defines repository interfaces for data persistence. It provides abstractions for user and execution storage.

Package database defines the repository interfaces for data persistence. This file contains the SecretsRepository interface for secret metadata management.

Index

Variables

Errors for secrets operations

var (
    ErrSecretNotFound      = appErrors.ErrSecretNotFound("secret not found", nil)
    ErrSecretAlreadyExists = appErrors.ErrSecretAlreadyExists("secret already exists", nil)
)

type ConnectionRepository

ConnectionRepository defines the interface for WebSocket connection-related database operations.

type ConnectionRepository interface {
    // CreateConnection stores a new WebSocket connection record in the database.
    CreateConnection(ctx context.Context, connection *api.WebSocketConnection) error

    // DeleteConnections removes WebSocket connections from the database.
    DeleteConnections(ctx context.Context, connectionIDs []string) (int, error)

    // GetConnectionsByExecutionID retrieves all active WebSocket connection records for a given execution ID.
    // Returns the complete connection objects including token and other metadata.
    GetConnectionsByExecutionID(ctx context.Context, executionID string) ([]*api.WebSocketConnection, error)
}

type ExecutionRepository

ExecutionRepository defines the interface for execution-related database operations.

type ExecutionRepository interface {
    // CreateExecution stores a new execution record in the database.
    CreateExecution(ctx context.Context, execution *api.Execution) error

    // GetExecution retrieves an execution by its execution ID.
    GetExecution(ctx context.Context, executionID string) (*api.Execution, error)

    // UpdateExecution updates an existing execution record.
    UpdateExecution(ctx context.Context, execution *api.Execution) error

    // ListExecutions returns executions from the database with optional filtering and pagination.
    // Parameters:
    //   - limit: maximum number of executions to return. Use 0 to fetch all executions.
    //   - statuses: optional slice of execution statuses to filter by.
    //              If empty, all executions are returned.
    // Results are ordered newest first.
    ListExecutions(ctx context.Context, limit int, statuses []string) ([]*api.Execution, error)

    // GetExecutionsByRequestID retrieves all executions created or modified by a specific request ID.
    GetExecutionsByRequestID(ctx context.Context, requestID string) ([]*api.Execution, error)
}

type ImageRepository

ImageRepository defines the interface for image metadata storage operations.

type ImageRepository interface {
    // GetImagesByRequestID retrieves all images created or modified by a specific request ID.
    GetImagesByRequestID(ctx context.Context, requestID string) ([]api.ImageInfo, error)
}

type Repositories

Repositories groups all database repository interfaces together. This struct is used to pass repositories as a cohesive unit while maintaining explicit access to individual repositories in service methods.

type Repositories struct {
    User       UserRepository
    Execution  ExecutionRepository
    Connection ConnectionRepository
    Token      TokenRepository
    Image      ImageRepository
    Secrets    SecretsRepository
}

type SecretsRepository

SecretsRepository defines the interface for persisting secret data. Implementations handle storing and retrieving secrets in their preferred storage backend.

type SecretsRepository interface {
    // CreateSecret stores a new secret.
    // The secret's CreatedBy field must be set by the caller.
    // The repository sets CreatedAt and UpdatedAt timestamps.
    // Returns an error if a secret with the same name already exists.
    CreateSecret(ctx context.Context, secret *api.Secret) error

    // GetSecret retrieves a secret by name.
    // If includeValue is true, the secret value will be decrypted and included in the response.
    // Returns an error if the secret is not found.
    GetSecret(ctx context.Context, name string, includeValue bool) (*api.Secret, error)

    // ListSecrets retrieves all secrets.
    // If includeValue is true, secret values will be decrypted and included in the response.
    ListSecrets(ctx context.Context, includeValue bool) ([]*api.Secret, error)

    // UpdateSecret updates a secret's value and/or editable properties.
    // The secret's UpdatedBy field must be set by the caller.
    // The Name field identifies which secret to update.
    // The updatedAt timestamp is always refreshed.
    // Returns an error if the secret is not found.
    UpdateSecret(ctx context.Context, secret *api.Secret) error

    // DeleteSecret removes a secret from storage.
    // Returns an error if the secret is not found.
    DeleteSecret(ctx context.Context, name string) error

    // GetSecretsByRequestID retrieves all secrets created or modified by a specific request ID.
    GetSecretsByRequestID(ctx context.Context, requestID string) ([]*api.Secret, error)
}

type TokenRepository

TokenRepository defines the interface for WebSocket token validation operations.

type TokenRepository interface {
    // CreateToken stores a new WebSocket authentication token with metadata.
    CreateToken(ctx context.Context, token *api.WebSocketToken) error

    // GetToken retrieves a token by its value and validates it hasn't expired.
    // Returns nil if the token doesn't exist or has expired (DynamoDB TTL handles expiration).
    GetToken(ctx context.Context, tokenValue string) (*api.WebSocketToken, error)

    // DeleteToken removes a token from the database (used after validation or explicit cleanup).
    DeleteToken(ctx context.Context, tokenValue string) error
}

type UserRepository

UserRepository defines the interface for user-related database operations. This abstraction allows for different implementations (DynamoDB, PostgreSQL, etc.) without changing the business logic layer.

type UserRepository interface {
    // CreateUser stores a new user with their hashed API key in the database.
    // If expiresAtUnix is 0, no TTL is set (permanent user).
    // If expiresAtUnix is > 0, it sets expires_at for automatic deletion.
    // Returns an error if the user already exists or if the operation fails.
    CreateUser(ctx context.Context, user *api.User, apiKeyHash string, expiresAtUnix int64) error

    // RemoveExpiration removes the expires_at field from a user record, making them permanent.
    RemoveExpiration(ctx context.Context, email string) error

    // GetUserByEmail retrieves a user by their email address.
    // Returns nil if the user doesn't exist.
    GetUserByEmail(ctx context.Context, email string) (*api.User, error)

    // GetUserByAPIKeyHash retrieves a user by their hashed API key.
    // Used for authentication. Returns nil if no user has this API key.
    GetUserByAPIKeyHash(ctx context.Context, apiKeyHash string) (*api.User, error)

    // UpdateLastUsed updates the last_used timestamp for a user.
    // Called after successful API key authentication.
    UpdateLastUsed(ctx context.Context, email string) (*time.Time, error)

    // RevokeUser marks a user's API key as revoked without deleting the record.
    // Useful for audit trails.
    RevokeUser(ctx context.Context, email string) error

    // CreatePendingAPIKey stores a pending API key with a secret token.
    CreatePendingAPIKey(ctx context.Context, pending *api.PendingAPIKey) error

    // GetPendingAPIKey retrieves a pending API key by its secret token.
    // Returns nil if the token doesn't exist or has expired.
    GetPendingAPIKey(ctx context.Context, secretToken string) (*api.PendingAPIKey, error)

    // MarkAsViewed atomically marks a pending key as viewed with the IP address.
    MarkAsViewed(ctx context.Context, secretToken string, ipAddress string) error

    // DeletePendingAPIKey removes a pending API key from the database.
    DeletePendingAPIKey(ctx context.Context, secretToken string) error

    // ListUsers returns all users in the system (excluding API key hashes for security).
    // Used by admins to view all users and their basic information.
    ListUsers(ctx context.Context) ([]*api.User, error)

    // GetUsersByRequestID retrieves all users created or modified by a specific request ID.
    GetUsersByRequestID(ctx context.Context, requestID string) ([]*api.User, error)
}

errors

import "runvoy/internal/errors"

Package errors provides error types and handling for runvoy. It includes custom error types with HTTP status codes and error codes.

Index

Constants

Predefined error codes

const (
    // Client error codes
    ErrCodeInvalidRequest = "INVALID_REQUEST"
    ErrCodeUnauthorized   = "UNAUTHORIZED"
    ErrCodeForbidden      = "FORBIDDEN"
    ErrCodeNotFound       = "NOT_FOUND"
    ErrCodeConflict       = "CONFLICT"
    ErrCodeSecretNotFound = "SECRET_NOT_FOUND"
    ErrCodeSecretExists   = "SECRET_ALREADY_EXISTS"
    ErrCodeInvalidAPIKey  = "INVALID_API_KEY" //nolint:gosec
    ErrCodeAPIKeyRevoked  = "API_KEY_REVOKED" //nolint:gosec

    // Server error codes
    ErrCodeInternalError      = "INTERNAL_ERROR"
    ErrCodeDatabaseError      = "DATABASE_ERROR"
    ErrCodeServiceUnavailable = "SERVICE_UNAVAILABLE"
)

func GetErrorCode

func GetErrorCode(err error) string

GetErrorCode extracts the error code from an error. Returns empty string if the error is not an AppError.

func GetErrorDetails

func GetErrorDetails(err error) string

GetErrorDetails extracts detailed error information including the underlying cause. Returns the underlying error message if available, otherwise returns the main error message.

func GetErrorMessage

func GetErrorMessage(err error) string

GetErrorMessage extracts a user-friendly message from an error.

func GetStatusCode

func GetStatusCode(err error) int

GetStatusCode extracts the HTTP status code from an error. Returns 500 if the error is not an AppError.

type AppError

AppError represents an application error with an associated HTTP status code.

type AppError struct {
    // Code is an optional error code string for programmatic handling
    Code string
    // Message is a user-friendly error message
    Message string
    // StatusCode is the HTTP status code to return
    StatusCode int
    // Cause is the underlying error (for error wrapping)
    Cause error
}

func ErrAPIKeyRevoked

func ErrAPIKeyRevoked(cause error) *AppError

ErrAPIKeyRevoked creates an API key revoked error (401).

func ErrBadRequest

func ErrBadRequest(message string, cause error) *AppError

ErrBadRequest creates a bad request error (400).

func ErrConflict

func ErrConflict(message string, cause error) *AppError

ErrConflict creates a conflict error (409).

func ErrDatabaseError

func ErrDatabaseError(message string, cause error) *AppError

ErrDatabaseError creates a database error (503 Service Unavailable). Database failures are typically transient issues.

func ErrForbidden

func ErrForbidden(message string, cause error) *AppError

ErrForbidden creates a forbidden error (403).

func ErrInternalError

func ErrInternalError(message string, cause error) *AppError

ErrInternalError creates an internal server error (500).

func ErrInvalidAPIKey

func ErrInvalidAPIKey(cause error) *AppError

ErrInvalidAPIKey creates an invalid API key error (401).

func ErrNotFound

func ErrNotFound(message string, cause error) *AppError

ErrNotFound creates a not found error (404).

func ErrSecretAlreadyExists

func ErrSecretAlreadyExists(message string, cause error) *AppError

ErrSecretAlreadyExists creates a secret already exists error (409).

func ErrSecretNotFound

func ErrSecretNotFound(message string, cause error) *AppError

ErrSecretNotFound creates a secret not found error (404).

func ErrServiceUnavailable

func ErrServiceUnavailable(message string, cause error) *AppError

ErrServiceUnavailable creates a service unavailable error (503). Use this for resources that are temporarily unavailable but may become available soon.

func ErrUnauthorized

func ErrUnauthorized(message string, cause error) *AppError

ErrUnauthorized creates an unauthorized error (401).

func NewClientError

func NewClientError(statusCode int, code, message string, cause error) *AppError

NewClientError creates a new client error (4xx status codes).

func NewServerError

func NewServerError(statusCode int, code, message string, cause error) *AppError

NewServerError creates a new server error (5xx status codes).

func (*AppError) Error

func (e *AppError) Error() string

Error implements the error interface.

func (*AppError) Is

func (e *AppError) Is(target error) bool

Is allows errors.Is to work with AppError.

func (*AppError) Unwrap

func (e *AppError) Unwrap() error

Unwrap returns the underlying error for error unwrapping.

logger

import "runvoy/internal/logger"

Package logger provides structured logging utilities for runvoy. It includes context-aware logging and log level management.

Index

func ClearContextExtractors

func ClearContextExtractors()

ClearContextExtractors removes all registered context extractors. This is primarily useful for testing.

func DeriveRequestLogger

func DeriveRequestLogger(ctx context.Context, base *slog.Logger) *slog.Logger

DeriveRequestLogger returns a logger enriched with request-scoped fields available in the provided context. It first checks for a request ID set via WithRequestID, then tries all registered ContextExtractors in order. This allows supporting multiple providers (AWS Lambda, HTTP servers, etc.) without hardcoding provider-specific logic.

IMPORTANT: The requestID field is always added at the root level of the log entry (never nested in a "context" object). This ensures we can query logs by requestID directly using e.g. filter requestID = "value". Do NOT add requestID to any "context" map objects in log calls.

func ExtractRequestIDFromContext

func ExtractRequestIDFromContext(ctx context.Context) string

ExtractRequestIDFromContext attempts to extract a request ID from the context using the same priority as DeriveRequestLogger: first checks for a request ID set via WithRequestID, then tries all registered ContextExtractors in order. Returns the first request ID found, or an empty string if none is found. This allows middleware to extract request IDs without knowing about provider-specific implementations (e.g., AWS Lambda).

func GetDeadlineInfo

func GetDeadlineInfo(ctx context.Context) []any

GetDeadlineInfo returns logging attributes for context deadline information. Returns the absolute deadline time and remaining duration if set, or "none" if no deadline.

func GetRequestID

func GetRequestID(ctx context.Context) string

GetRequestID extracts the request ID from the context. The request ID is set by server middleware when available.

func Initialize

func Initialize(env constants.Environment, level slog.Level) *slog.Logger

Initialize sets up the global slog logger based on the environment

func RegisterContextExtractor

func RegisterContextExtractor(extractor ContextExtractor)

RegisterContextExtractor registers a new context extractor. Extractors are called in the order they are registered.

func SliceToMap

func SliceToMap(args []any) map[string]any

SliceToMap converts a slice of alternating key-value pairs to a map[string]any. It expects the slice to have an even number of elements with string keys. Non-string keys are skipped.

func WithRequestID

func WithRequestID(ctx context.Context, requestID string) context.Context

WithRequestID returns a new context with the given request ID attached. This should be used by server middleware to add request IDs to the context.

type ContextExtractor

ContextExtractor defines an interface for extracting request IDs from various context sources (e.g., AWS Lambda, HTTP servers, other cloud providers). This interface allows the logger to remain portable and not bound to any specific provider implementation.

type ContextExtractor interface {
    // ExtractRequestID attempts to extract a request ID from the given context.
    // Returns the request ID and true if found, empty string and false otherwise.
    ExtractRequestID(ctx context.Context) (requestID string, found bool)
}

secrets

import "runvoy/internal/secrets"

Package secrets provides shared utilities for secret detection and handling.

Index

Variables

DefaultSecretPatterns contains the default patterns used to identify environment variable names that should be treated as secrets.

var DefaultSecretPatterns = []string{
    "ACCESS_KEY",
    "API_KEY",
    "API_SECRET",
    "GITHUB_SECRET",
    "GITHUB_TOKEN",
    "PASSWORD",
    "PRIVATE_KEY",
    "SECRET_KEY",
    "SECRET",
    "TOKEN",
}

func GetSecretVariableNames

func GetSecretVariableNames(env map[string]string) []string

GetSecretVariableNames returns a list of variable names from the given environment that should be treated as secrets based on pattern matching. These variables will be processed without exposing their values in logs.

func MergeSecretVarNames

func MergeSecretVarNames(known, detected []string) []string

MergeSecretVarNames merges known secret variable names with pattern-detected ones, removing duplicates. This allows combining explicitly known secrets with pattern-based detection for comprehensive coverage.

server

import "runvoy/internal/server"

Package server implements the HTTP server and handlers for runvoy. It provides REST API endpoints for user management and command execution.

Index

type Router

Router wraps a chi router with service dependencies for handling API requests.

type Router struct {
    // contains filtered or unexported fields
}

func NewRouter

func NewRouter(svc *orchestrator.Service, requestTimeout time.Duration, allowedOrigins []string) *Router

NewRouter creates a new chi router with routes configured. If requestTimeout is > 0, adds a per-request timeout middleware. If requestTimeout is 0, no timeout middleware is added, allowing the environment (e.g., Lambda with its own timeout) to handle timeouts.

func (*Router) ChiMux

func (r *Router) ChiMux() *chi.Mux

ChiMux returns the underlying chi router for advanced usage

func (*Router) GetLoggerFromContext

func (r *Router) GetLoggerFromContext(ctx context.Context) *slog.Logger

GetLoggerFromContext extracts the logger from request context Returns the request-scoped logger (with request ID if available) or falls back to service logger

func (*Router) Handler

func (r *Router) Handler() http.Handler

Handler returns an http.Handler for the router

func (*Router) ServeHTTP

func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request)

ServeHTTP implements http.Handler for use with chi router

func (*Router) WithContext

func (r *Router) WithContext(ctx context.Context, svc *orchestrator.Service) context.Context

WithContext adds the service to the request context

testutil

import "runvoy/internal/testutil"

Package testutil provides shared testing utilities and helpers.

Index

func AssertAppErrorCode

func AssertAppErrorCode(t *testing.T, err error, expectedCode string, _ ...any) bool

AssertAppErrorCode checks if the error has a specific error code.

func AssertAppErrorStatus

func AssertAppErrorStatus(t *testing.T, err error, expectedStatus int, _ ...any) bool

AssertAppErrorStatus checks if the error has a specific HTTP status code.

func AssertEqual

func AssertEqual(t *testing.T, expected, actual any, msgAndArgs ...any) bool

AssertEqual is a wrapper around assert.Equal with context.

func AssertError

func AssertError(t *testing.T, err error, msgAndArgs ...any) bool

AssertError is a wrapper around assert.Error with context.

func AssertErrorType

func AssertErrorType(t *testing.T, err, target error, _ ...any) bool

AssertErrorType checks if the error is of a specific type using errors.Is.

func AssertNil

func AssertNil(t *testing.T, obj any, msgAndArgs ...any) bool

AssertNil is a wrapper around assert.Nil with context.

func AssertNoError

func AssertNoError(t *testing.T, err error, msgAndArgs ...any) bool

AssertNoError is a wrapper around assert.NoError with context.

func AssertNotEmpty

func AssertNotEmpty(t *testing.T, obj any, msgAndArgs ...any) bool

AssertNotEmpty is a wrapper around assert.NotEmpty with context.

func AssertNotNil

func AssertNotNil(t *testing.T, obj any, msgAndArgs ...any) bool

AssertNotNil is a wrapper around assert.NotNil with context.

func SilentLogger

func SilentLogger() *slog.Logger

SilentLogger creates a logger that discards all output.

func TestContext

func TestContext() context.Context

TestContext creates a test context with a reasonable timeout. Note: The cancel function is intentionally not returned since test contexts are expected to be short-lived and will be cleaned up when the test completes.

func TestLogger

func TestLogger() *slog.Logger

TestLogger creates a logger suitable for testing (outputs to stderr).

type ExecutionBuilder

ExecutionBuilder provides a fluent interface for building test executions.

type ExecutionBuilder struct {
    // contains filtered or unexported fields
}

func NewExecutionBuilder

func NewExecutionBuilder() *ExecutionBuilder

NewExecutionBuilder creates a new ExecutionBuilder with sensible defaults.

func (*ExecutionBuilder) Build

func (b *ExecutionBuilder) Build() *api.Execution

Build returns the constructed Execution.

func (*ExecutionBuilder) Completed

func (b *ExecutionBuilder) Completed() *ExecutionBuilder

Completed marks the execution as completed.

func (*ExecutionBuilder) Failed

func (b *ExecutionBuilder) Failed() *ExecutionBuilder

Failed marks the execution as failed.

func (*ExecutionBuilder) WithCommand

func (b *ExecutionBuilder) WithCommand(cmd string) *ExecutionBuilder

WithCommand sets the execution command.

func (*ExecutionBuilder) WithCreatedBy

func (b *ExecutionBuilder) WithCreatedBy(email string) *ExecutionBuilder

WithCreatedBy sets the creator email.

func (*ExecutionBuilder) WithExecutionID

func (b *ExecutionBuilder) WithExecutionID(id string) *ExecutionBuilder

WithExecutionID sets the execution ID.

func (*ExecutionBuilder) WithLogStreamName

func (b *ExecutionBuilder) WithLogStreamName(name string) *ExecutionBuilder

WithLogStreamName sets the log stream name.

func (*ExecutionBuilder) WithStatus

func (b *ExecutionBuilder) WithStatus(status string) *ExecutionBuilder

WithStatus sets the execution status.

type UserBuilder

UserBuilder provides a fluent interface for building test users.

type UserBuilder struct {
    // contains filtered or unexported fields
}

func NewUserBuilder

func NewUserBuilder() *UserBuilder

NewUserBuilder creates a new UserBuilder with sensible defaults.

func (*UserBuilder) Build

func (b *UserBuilder) Build() *api.User

Build returns the constructed User.

func (*UserBuilder) Revoked

func (b *UserBuilder) Revoked() *UserBuilder

Revoked marks the user as revoked.

func (*UserBuilder) WithCreatedAt

func (b *UserBuilder) WithCreatedAt(t time.Time) *UserBuilder

WithCreatedAt sets the user's creation time.

func (*UserBuilder) WithEmail

func (b *UserBuilder) WithEmail(email string) *UserBuilder

WithEmail sets the user's email.

func (*UserBuilder) WithLastUsed

func (b *UserBuilder) WithLastUsed(t time.Time) *UserBuilder

WithLastUsed sets the user's last used time.

func (*UserBuilder) WithRole

func (b *UserBuilder) WithRole(role string) *UserBuilder

WithRole sets the user's role.

axiom-forwarder

import "runvoy/scripts/axiom-forwarder"

Package main implements an AWS Lambda function that forwards CloudWatch Logs to Axiom. This is a standalone utility kept in the runvoy repository for convenience.

Index

create-config-file

import "runvoy/scripts/create-config-file"

Package main provides a utility to create a configuration file for runvoy.

Index

delete-s3-buckets

import "runvoy/scripts/delete-s3-buckets"

Package main provides a utility script to delete non-empty S3 buckets. It deletes all object versions, delete markers, and objects before deleting the bucket.

Index

generate-cli-docs

import "runvoy/scripts/generate-cli-docs"

Package main provides a utility to generate a single markdown file documenting all CLI commands.

Index

seed-admin-user

import "runvoy/scripts/seed-admin-user"

seed-admin-user is a utility script to seed the admin user into the database. This script is intentionally kept for operational purposes (initial setup, recovery, etc.).

Index

sync-env-vars

import "runvoy/scripts/sync-env-vars"

Package main provides a utility to synchronize environment variables between Lambda functions and local .env files.

Index

truncate-dynamodb-table

import "runvoy/scripts/truncate-dynamodb-table"

Package main provides a utility script to truncate (delete all records from) a DynamoDB table. It scans all items from the table and deletes them in batches.

Index

update-readme-help

import "runvoy/scripts/update-readme-help"

Package main provides a utility to update the README.md with the latest CLI help output and version.

Index

cmd

import "runvoy/cmd/cli/cmd"

Package cmd implements the CLI commands for the runvoy tool.

Index

func Execute

func Execute()

Execute runs the root command and handles cleanup of timeout context.

func RootCmd

func RootCmd() *cobra.Command

RootCmd returns the root command for use by tools like doc generators.

type ClaimService

ClaimService handles API key claiming logic

type ClaimService struct {
    // contains filtered or unexported fields
}

func NewClaimService

func NewClaimService(apiClient client.Interface, outputter OutputInterface, configSaver ConfigSaver) *ClaimService

NewClaimService creates a new ClaimService with the provided dependencies

func (*ClaimService) ClaimAPIKey

func (s *ClaimService) ClaimAPIKey(ctx context.Context, token string, cfg *config.Config) error

ClaimAPIKey claims an API key using the provided token and saves it to the config

type ConfigLoader

ConfigLoader defines an interface for loading configuration

type ConfigLoader interface {
    Load() (*config.Config, error)
}

func NewConfigLoader

func NewConfigLoader() ConfigLoader

NewConfigLoader creates a ConfigLoader using the global config.Load function

type ConfigLoaderFunc

ConfigLoaderFunc adapts a function to the ConfigLoader interface

type ConfigLoaderFunc func() (*config.Config, error)

func (ConfigLoaderFunc) Load

func (f ConfigLoaderFunc) Load() (*config.Config, error)

Load executes the underlying function to load configuration

type ConfigPathGetter

ConfigPathGetter defines an interface for retrieving the configuration path

type ConfigPathGetter interface {
    GetConfigPath() (string, error)
}

func NewConfigPathGetter

func NewConfigPathGetter() ConfigPathGetter

NewConfigPathGetter creates a ConfigPathGetter using the global config.GetConfigPath function

type ConfigPathGetterFunc

ConfigPathGetterFunc adapts a function to the ConfigPathGetter interface

type ConfigPathGetterFunc func() (string, error)

func (ConfigPathGetterFunc) GetConfigPath

func (f ConfigPathGetterFunc) GetConfigPath() (string, error)

GetConfigPath executes the underlying function to retrieve the config path

type ConfigSaver

ConfigSaver defines an interface for saving configuration

type ConfigSaver interface {
    Save(*config.Config) error
}

func NewConfigSaver

func NewConfigSaver() ConfigSaver

NewConfigSaver creates a ConfigSaver using the global config.Save function

type ConfigSaverFunc

ConfigSaverFunc adapts a function to the ConfigSaver interface

type ConfigSaverFunc func(*config.Config) error

func (ConfigSaverFunc) Save

func (f ConfigSaverFunc) Save(cfg *config.Config) error

Save executes the underlying function to persist configuration

type ConfigureService

ConfigureService handles configuration logic

type ConfigureService struct {
    // contains filtered or unexported fields
}

func NewConfigureService

func NewConfigureService(outputter OutputInterface, configSaver ConfigSaver, configLoader ConfigLoader, configPathGetter ConfigPathGetter) *ConfigureService

NewConfigureService creates a new ConfigureService with the provided dependencies

func (*ConfigureService) Configure

func (s *ConfigureService) Configure(_ context.Context) error

Configure runs the interactive configuration flow

type ExecuteCommandRequest

ExecuteCommandRequest contains all parameters needed to execute a command

type ExecuteCommandRequest struct {
    Command string
    GitRepo string
    GitRef  string
    GitPath string
    Image   string
    Env     map[string]string
    Secrets []string
    WebURL  string
}

type ImagesService

ImagesService handles image management logic

type ImagesService struct {
    // contains filtered or unexported fields
}

func NewImagesService

func NewImagesService(apiClient client.Interface, outputter OutputInterface) *ImagesService

NewImagesService creates a new ImagesService with the provided dependencies

func (*ImagesService) ListImages

func (s *ImagesService) ListImages(ctx context.Context) error

ListImages lists all registered images

func (*ImagesService) RegisterImage

func (s *ImagesService) RegisterImage(ctx context.Context, image string, isDefault *bool, taskRoleName, taskExecutionRoleName *string, cpu, memory *int, runtimePlatform *string) error

RegisterImage registers a new image

func (*ImagesService) ShowImage

func (s *ImagesService) ShowImage(ctx context.Context, image string) error

ShowImage shows detailed information about a single image

func (*ImagesService) UnregisterImage

func (s *ImagesService) UnregisterImage(ctx context.Context, image string) error

UnregisterImage unregisters an image

type KillService

KillService handles execution killing logic

type KillService struct {
    // contains filtered or unexported fields
}

func NewKillService

func NewKillService(apiClient client.Interface, outputter OutputInterface) *KillService

NewKillService creates a new KillService with the provided dependencies

func (*KillService) KillExecution

func (s *KillService) KillExecution(ctx context.Context, executionID string) error

KillExecution kills a running execution and displays the results

type ListService

ListService handles execution listing and formatting logic

type ListService struct {
    // contains filtered or unexported fields
}

func NewListService

func NewListService(apiClient client.Interface, outputter OutputInterface) *ListService

NewListService creates a new ListService with the provided dependencies

func (*ListService) ListExecutions

func (s *ListService) ListExecutions(ctx context.Context, limit int, statuses string) error

ListExecutions lists executions with optional filtering and displays them in a table format

type LogsService

LogsService handles log display logic

type LogsService struct {
    // contains filtered or unexported fields
}

func NewLogsService

func NewLogsService(apiClient client.Interface, outputter OutputInterface, sleeper Sleeper) *LogsService

NewLogsService creates a new LogsService with the provided dependencies

func (*LogsService) DisplayLogs

func (s *LogsService) DisplayLogs(ctx context.Context, executionID, webURL string) error

DisplayLogs retrieves static logs and then streams new logs via WebSocket in real-time If the execution has already completed, it displays static logs only and skips WebSocket streaming

type OutputInterface

OutputInterface defines the interface for output operations to enable dependency injection and testing

type OutputInterface interface {
    Infof(format string, a ...any)
    Errorf(format string, a ...any)
    Successf(format string, a ...any)
    Warningf(format string, a ...any)
    Table(headers []string, rows [][]string)
    Blank()
    Bold(text string) string
    Cyan(text string) string
    KeyValue(key, value string)
    Prompt(prompt string) string
}

func NewOutputWrapper

func NewOutputWrapper() OutputInterface

NewOutputWrapper creates a new output wrapper that implements OutputInterface

type PlaybookOverrides

PlaybookOverrides contains values to override in a playbook

type PlaybookOverrides struct {
    Image   string
    GitRepo string
    GitRef  string
    GitPath string
    Secrets []string
}

type PlaybookService

PlaybookService handles playbook operations

type PlaybookService struct {
    // contains filtered or unexported fields
}

func NewPlaybookService

func NewPlaybookService(loader *playbooks.PlaybookLoader, executor *playbooks.PlaybookExecutor, outputter OutputInterface) *PlaybookService

NewPlaybookService creates a new PlaybookService

func (*PlaybookService) ListPlaybooks

func (s *PlaybookService) ListPlaybooks(_ context.Context) error

ListPlaybooks lists all available playbooks

func (*PlaybookService) RunPlaybook

func (s *PlaybookService) RunPlaybook(ctx context.Context, name string, userEnv map[string]string, overrides *PlaybookOverrides, webURL string, runService *RunService) error

RunPlaybook executes a playbook with optional overrides

func (*PlaybookService) ShowPlaybook

func (s *PlaybookService) ShowPlaybook(_ context.Context, name string) error

ShowPlaybook displays the full content of a playbook

type RealSleeper

RealSleeper implements Sleeper using the standard time.Sleep

type RealSleeper struct{}

func (*RealSleeper) Sleep

func (r *RealSleeper) Sleep(duration time.Duration)

Sleep pauses execution for the specified duration

type RunService

RunService handles command execution logic

type RunService struct {
    // contains filtered or unexported fields
}

func NewRunService

func NewRunService(apiClient client.Interface, outputter OutputInterface) *RunService

NewRunService creates a new RunService with the provided dependencies

func (*RunService) ExecuteCommand

func (s *RunService) ExecuteCommand(ctx context.Context, req *ExecuteCommandRequest) error

ExecuteCommand executes a command remotely and displays the results

type SecretsService

SecretsService handles secrets management logic

type SecretsService struct {
    // contains filtered or unexported fields
}

func NewSecretsService

func NewSecretsService(apiClient client.Interface, outputter OutputInterface) *SecretsService

NewSecretsService creates a new SecretsService with the provided dependencies

func (*SecretsService) CreateSecret

func (s *SecretsService) CreateSecret(ctx context.Context, name, keyName, value, description string) error

CreateSecret creates a new secret with the given name, key name, value, and optional description

func (*SecretsService) DeleteSecret

func (s *SecretsService) DeleteSecret(ctx context.Context, name string) error

DeleteSecret deletes a secret by name

func (*SecretsService) GetSecret

func (s *SecretsService) GetSecret(ctx context.Context, name string) error

GetSecret retrieves a secret by name

func (*SecretsService) ListSecrets

func (s *SecretsService) ListSecrets(ctx context.Context) error

ListSecrets lists all secrets and displays them in a table format

func (*SecretsService) UpdateSecret

func (s *SecretsService) UpdateSecret(ctx context.Context, name, keyName, value, description string) error

UpdateSecret updates a secret's metadata and/or value

type Sleeper

Sleeper provides a testable interface for introducing delays

type Sleeper interface {
    Sleep(duration time.Duration)
}

type StatusService

StatusService handles status display logic

type StatusService struct {
    // contains filtered or unexported fields
}

func NewStatusService

func NewStatusService(apiClient client.Interface, outputter OutputInterface) *StatusService

NewStatusService creates a new StatusService with the provided dependencies

func (*StatusService) DisplayStatus

func (s *StatusService) DisplayStatus(ctx context.Context, executionID string) error

DisplayStatus retrieves and displays the status of an execution

type TraceService

TraceService handles backend logs display logic

type TraceService struct {
    // contains filtered or unexported fields
}

func NewTraceService

func NewTraceService(apiClient client.Interface, outputter OutputInterface) *TraceService

NewTraceService creates a new TraceService with the provided dependencies

func (*TraceService) DisplayBackendLogs

func (s *TraceService) DisplayBackendLogs(ctx context.Context, requestID string) error

DisplayBackendLogs retrieves and displays backend infrastructure logs and related resources for a request ID

type UsersService

UsersService handles user management logic

type UsersService struct {
    // contains filtered or unexported fields
}

func NewUsersService

func NewUsersService(apiClient client.Interface, outputter OutputInterface) *UsersService

NewUsersService creates a new UsersService with the provided dependencies

func (*UsersService) CreateUser

func (s *UsersService) CreateUser(ctx context.Context, email, role string) error

CreateUser creates a new user with the given email and role

func (*UsersService) ListUsers

func (s *UsersService) ListUsers(ctx context.Context) error

ListUsers lists all users and displays them in a table format

func (*UsersService) RevokeUser

func (s *UsersService) RevokeUser(ctx context.Context, email string) error

RevokeUser revokes a user's API key

server

import "runvoy/cmd/local/server"

Package server provides the async event processor HTTP server setup.

Index

func NewRouter

func NewRouter(proc processor.Processor, log *slog.Logger) *chi.Mux

NewRouter creates a chi router for the async event processor.

authorization

import "runvoy/internal/auth/authorization"

Package authorization provides Casbin-based authorization enforcement for runvoy. It implements role-based access control (RBAC) with resource ownership support.

Index

Variables

CasbinFS embeds the Casbin model and policy files into the binary. This allows the application to run without requiring these files to be present on the filesystem.

var CasbinFS embed.FS

func FormatResourceID

func FormatResourceID(resourceType, resourceID string) string

FormatResourceID converts a resource type and ID to the Casbin resource format. Example: FormatResourceID("secret", "secret-123") returns "secret:secret-123"

func FormatRole

func FormatRole(role Role) string

FormatRole converts a role to the Casbin role format. Example: FormatRole(RoleAdmin) returns "role:admin"

func IsValidRole

func IsValidRole(roleStr string) bool

IsValidRole checks if a role name string is valid.

func NewEmbeddedAdapter

func NewEmbeddedAdapter(fsys fs.FS, pathBase string) persist.Adapter

NewEmbeddedAdapter creates a new adapter for reading Casbin config from an embedded filesystem. The adapter supports in-memory policy modifications at runtime but does not persist changes back to the embedded files, as they are read-only.

func ValidRoles

func ValidRoles() []string

ValidRoles returns a list of all valid role names as strings.

type Action

Action is a typed string representing an action in the authorization system.

type Action string

Action constants for Casbin enforcement. These correspond to the HTTP methods mapped to CRUD actions.

const (
    ActionCreate Action = "create"
    ActionRead   Action = "read"
    ActionUpdate Action = "update"
    ActionDelete Action = "delete"
    ActionKill   Action = "kill"
    ActionUse    Action = "use"
)

type Enforcer

Enforcer wraps the Casbin enforcer with additional functionality.

type Enforcer struct {
    // contains filtered or unexported fields
}

func NewEnforcer

func NewEnforcer(log *slog.Logger) (*Enforcer, error)

NewEnforcer creates a new Casbin enforcer using embedded Casbin configuration files. The model and policy are embedded in the binary at build time, so no filesystem access is required.

func (*Enforcer) AddOwnershipForResource

func (e *Enforcer) AddOwnershipForResource(ctx context.Context, resourceID, ownerEmail string) error

AddOwnershipForResource adds ownership mapping for a resource. This allows the owner to access their own resources.

Example usage:

err := e.AddOwnershipForResource(ctx, "secret:secret-123", "user@example.com")

func (*Enforcer) AddRoleForUser

func (e *Enforcer) AddRoleForUser(ctx context.Context, user string, role Role) error

AddRoleForUser assigns a role to a user. Returns an error if the role is invalid or empty.

Example usage:

err := e.AddRoleForUser(ctx, "user@example.com", RoleDeveloper)

func (*Enforcer) Enforce

func (e *Enforcer) Enforce(ctx context.Context, subject, object string, action Action) (bool, error)

Enforce checks if a subject (user) can perform an action on an object (resource). Returns true if the action is allowed, false otherwise.

Example usage:

allowed, err := e.Enforce(ctx, "user@example.com", "/api/secrets/secret-123", "read")

func (*Enforcer) GetAllNamedGroupingPolicies

func (e *Enforcer) GetAllNamedGroupingPolicies(ptype string) ([][]string, error)

GetAllNamedGroupingPolicies returns all grouping policies for the given policy type (e.g., "g2" for ownership).

func (*Enforcer) GetRolesForUser

func (e *Enforcer) GetRolesForUser(user string) ([]string, error)

GetRolesForUser returns all roles assigned to a user.

func (*Enforcer) HasOwnershipForResource

func (e *Enforcer) HasOwnershipForResource(resourceID, ownerEmail string) (bool, error)

HasOwnershipForResource checks if the provided user currently owns the resource.

func (*Enforcer) Hydrate

func (e *Enforcer) Hydrate(ctx context.Context, userRepo database.UserRepository, executionRepo database.ExecutionRepository, secretsRepo database.SecretsRepository, imageRepo ImageRepository) error

Hydrate loads all user roles and resource ownerships into the Casbin enforcer. This should be called during initialization to populate the enforcer with current data.

func (*Enforcer) LoadResourceOwnerships

func (e *Enforcer) LoadResourceOwnerships(ctx context.Context, ownerships map[string]string) error

LoadResourceOwnerships loads resource ownership mappings into the enforcer.

func (*Enforcer) LoadRolesForUsers

func (e *Enforcer) LoadRolesForUsers(ctx context.Context, userRoles map[string]string) error

LoadRolesForUsers loads role assignments for multiple users into the enforcer. This is typically called at startup to initialize the enforcer with current user roles. The roleStr values should be valid role names (admin, operator, developer, viewer).

Example usage:

roles := map[string]string{
  "admin@example.com": "admin",
  "dev@example.com": "developer",
}
err := e.LoadRolesForUsers(ctx, roles)

func (*Enforcer) RemoveAllOwnershipsForResource

func (e *Enforcer) RemoveAllOwnershipsForResource(ctx context.Context, resourceID string) error

RemoveAllOwnershipsForResource removes every ownership mapping for the given resource identifier. This is useful when deleting a resource without knowing its owner ahead of time.

func (*Enforcer) RemoveOwnershipForResource

func (e *Enforcer) RemoveOwnershipForResource(ctx context.Context, resourceID, ownerEmail string) error

RemoveOwnershipForResource removes ownership mapping for a resource.

func (*Enforcer) RemoveRoleForUser

func (e *Enforcer) RemoveRoleForUser(ctx context.Context, user, role string) error

RemoveRoleForUser removes a role from a user.

Example usage:

err := e.RemoveRoleForUser(ctx, "user@example.com", "role:developer")

type ImageRepository

ImageRepository defines the interface for listing images. This is a minimal interface to avoid import cycles.

type ImageRepository interface {
    ListImages(ctx context.Context) ([]api.ImageInfo, error)
}

type Role

Role is a typed string representing a user role in the authorization system. Valid roles: admin, operator, developer, viewer

type Role string

Role constants for Casbin role-based access control. These correspond to the roles defined in casbin/policy.csv.

const (
    // RoleAdmin has full access to all resources and operations.
    RoleAdmin Role = "admin"

    // RoleOperator can manage images, secrets, and executions but cannot manage users.
    RoleOperator Role = "operator"

    // RoleDeveloper can create and manage their own resources and execute commands.
    RoleDeveloper Role = "developer"

    // RoleViewer has read-only access to executions.
    RoleViewer Role = "viewer"
)

func NewRole

func NewRole(roleStr string) (Role, error)

NewRole creates a new Role from a string, validating it against known roles. Returns an error if the role string is empty or not a valid role.

func (Role) String

func (r Role) String() string

String returns the string representation of the role.

func (Role) Valid

func (r Role) Valid() bool

Valid checks if the role is a valid known role.

contract

import "runvoy/internal/backend/contract"

Package contract defines all backend interfaces that providers must implement. These interfaces are separated into their own package to avoid circular dependencies between backend services (orchestrator, processor) and provider implementations. This package serves as the single source of truth for all backend provider dependencies.

Index

type HealthManager

HealthManager abstracts provider-specific health checks and resource reconciliation. This interface handles verifying and repairing inconsistencies between metadata storage and cloud resources.

type HealthManager interface {
    // Reconcile checks and repairs inconsistencies between metadata storage and actual cloud resources.
    // It verifies compute resources (e.g., task definitions, containers), secrets, and identity/access resources.
    // Returns a comprehensive health report with all issues found and actions taken.
    Reconcile(ctx context.Context) (*api.HealthReport, error)
}

type ImageRegistry

ImageRegistry abstracts provider-specific image management. This interface handles image registration, configuration, and lifecycle management.

type ImageRegistry interface {
    // RegisterImage registers a Docker image as a task definition in the execution platform.
    // isDefault: if true, explicitly set as default.
    // taskRoleName: optional custom task role name (if nil, uses default from config).
    // taskExecutionRoleName: optional custom task execution role name (if nil, uses default from config).
    // cpu: optional CPU value (e.g., 256, 1024). Defaults to 256 if nil.
    // memory: optional Memory value in MB (e.g., 512, 2048). Defaults to 512 if nil.
    // runtimePlatform: optional runtime platform (e.g., "Linux/ARM64", "Linux/X86_64"). Defaults to "Linux/ARM64" if nil.
    // createdBy: email of the user registering the image.
    RegisterImage(
        ctx context.Context,
        image string,
        isDefault *bool,
        taskRoleName, taskExecutionRoleName *string,
        cpu, memory *int,
        runtimePlatform *string,
        createdBy string,
    ) error
    // ListImages lists all registered Docker images.
    ListImages(ctx context.Context) ([]api.ImageInfo, error)

    // GetImage retrieves a single Docker image by ID or name.
    // Accepts either an ImageID (e.g., "alpine:latest-a1b2c3d4") or an image name (e.g., "alpine:latest").
    GetImage(ctx context.Context, image string) (*api.ImageInfo, error)

    // RemoveImage removes a Docker image and deregisters its task definitions.
    RemoveImage(ctx context.Context, image string) error
}

type LogManager

LogManager abstracts provider-specific execution log retrieval. This interface handles fetching logs from user task executions.

type LogManager interface {
    // FetchLogsByExecutionID retrieves execution logs for a specific execution.
    // Returns logs generated by the user's command execution in containers.
    // Returns empty slice if logs are not available or not supported by the provider.
    FetchLogsByExecutionID(ctx context.Context, executionID string) ([]api.LogEvent, error)
}

type ObservabilityManager

ObservabilityManager provides access to backend infrastructure logs and metrics. This interface is for platform debugging and observability, separate from user execution logs.

type ObservabilityManager interface {
    // FetchBackendLogs retrieves logs from the backend services for the provided requestID.
    // Returns logs from the backend services for debugging and tracing.
    FetchBackendLogs(ctx context.Context, requestID string) ([]api.LogEvent, error)
}

type TaskManager

TaskManager abstracts provider-specific task execution (e.g., AWS ECS, GCP Cloud Run, Azure Container Instances). This interface handles the core responsibility of running and managing task lifecycles.

type TaskManager interface {
    // StartTask triggers an execution on the underlying platform and returns
    // a stable executionID and the task creation timestamp.
    // The createdAt timestamp comes from the provider (e.g., ECS CreatedAt) when available.
    StartTask(
        ctx context.Context,
        userEmail string,
        req *api.ExecutionRequest) (executionID string, createdAt *time.Time, err error)
    // KillTask terminates a running task identified by executionID.
    // Returns an error if the task is already terminated or cannot be terminated.
    KillTask(ctx context.Context, executionID string) error
}

type WebSocketManager

WebSocketManager abstracts provider-specific WebSocket management. This interface handles WebSocket connection lifecycle and log streaming.

type WebSocketManager interface {
    // HandleRequest processes WebSocket lifecycle events (connect, disconnect, etc.).
    // Returns true if the event was handled, false otherwise.
    HandleRequest(ctx context.Context, rawEvent *json.RawMessage, reqLogger *slog.Logger) (bool, error)

    // NotifyExecutionCompletion sends disconnect notifications to all connected clients for an execution
    // and removes the connections.
    NotifyExecutionCompletion(ctx context.Context, executionID *string) error

    // SendLogsToExecution sends log events to all connected clients for an execution.
    SendLogsToExecution(ctx context.Context, executionID *string, logEvents []api.LogEvent) error

    // GenerateWebSocketURL creates a WebSocket token and returns the connection URL.
    // It stores the token for validation when the client connects.
    // Returns an empty string if URL generation fails (errors are logged).
    GenerateWebSocketURL(
        ctx context.Context,
        executionID string,
        userEmail *string,
        clientIPAtCreationTime *string,
    ) string
}

health

import "runvoy/internal/backend/health"

Package health provides health management functionality for runvoy. It defines the interface for reconciling resources between metadata storage and cloud provider services.

Index

type AuthorizerHealthStatus

AuthorizerHealthStatus contains the health status for authorization data.

type AuthorizerHealthStatus struct {
    UsersWithInvalidRoles      []string
    UsersWithMissingRoles      []string
    ResourcesWithMissingOwners []string
    OrphanedOwnerships         []string
    MissingOwnerships          []string
    TotalUsersChecked          int
    TotalResourcesChecked      int
}

type ComputeHealthStatus

ComputeHealthStatus contains the health status for compute resources (e.g., containers, task definitions).

type ComputeHealthStatus struct {
    TotalResources    int
    VerifiedCount     int
    RecreatedCount    int
    TagUpdatedCount   int
    OrphanedCount     int
    OrphanedResources []string
}

type IdentityHealthStatus

IdentityHealthStatus contains the health status for identity and access management resources.

type IdentityHealthStatus struct {
    DefaultRolesVerified bool
    CustomRolesVerified  int
    CustomRolesTotal     int
    MissingRoles         []string
}

type Issue

Issue represents a single health issue found during reconciliation.

type Issue struct {
    ResourceType string // Provider-specific resource type (e.g., "ecs_task_definition", "cloud_run_service")
    ResourceID   string
    Severity     string // "error", "warning"
    Message      string
    Action       string // "recreated", "requires_manual_intervention", "reported", "tag_updated"
}

type Manager

Manager defines the interface for health checks and resource reconciliation. Different cloud providers can implement this interface to support their specific infrastructure.

type Manager interface {
    // Reconcile checks and repairs inconsistencies between metadata storage and actual cloud resources.
    // It verifies compute resources (e.g., task definitions, containers), secrets, and identity/access resources.
    // Returns a comprehensive health report with all issues found and actions taken.
    Reconcile(ctx context.Context) (*Report, error)
}

type Report

Report contains the results of a health reconciliation run.

type Report struct {
    Timestamp        time.Time
    ComputeStatus    ComputeHealthStatus
    SecretsStatus    SecretsHealthStatus
    IdentityStatus   IdentityHealthStatus
    AuthorizerStatus AuthorizerHealthStatus
    Issues           []Issue
    ReconciledCount  int
    ErrorCount       int
}

type SecretsHealthStatus

SecretsHealthStatus contains the health status for secrets/parameters.

type SecretsHealthStatus struct {
    TotalSecrets       int
    VerifiedCount      int
    TagUpdatedCount    int
    MissingCount       int
    OrphanedCount      int
    OrphanedParameters []string
}

orchestrator

import "runvoy/internal/backend/orchestrator"

Package orchestrator provides the core orchestrator service for runvoy. This file contains health reconciliation functionality.

Package orchestrator provides the core orchestrator service for runvoy. It initializes and manages command execution and API request handling.

Index

type ImageConfig

ImageConfig provides a provider-agnostic way to configure image registration. It abstracts away provider-specific details like IAM roles (AWS), service accounts (GCP), etc.

type ImageConfig struct {
    // Image is the Docker image reference (e.g., "alpine:latest", "gcr.io/project/image:tag")
    Image string

    // IsDefault marks this image as the default for executions when no image is specified
    IsDefault *bool

    // Resources specifies compute resource requirements
    Resources *ResourceConfig

    // Runtime specifies platform and architecture requirements
    Runtime *RuntimeConfig

    // Permissions specifies execution permissions and roles
    Permissions *PermissionConfig

    // RegisteredBy tracks which user registered this image
    RegisteredBy string
}

type PermissionConfig

PermissionConfig abstracts execution permissions across providers. Different providers handle permissions differently: - AWS: TaskRole (app permissions) and TaskExecutionRole (infrastructure permissions) - GCP: Service Account - Azure: Managed Identity

type PermissionConfig struct {
    // TaskRole grants permissions to the running task/container
    // AWS: IAM role name, GCP: service account email, Azure: managed identity client ID
    TaskRole *string

    // ExecutionRole grants permissions to infrastructure to start the task
    // AWS-specific: IAM role for ECS to pull images, write logs, etc.
    // Not used by GCP or Azure
    ExecutionRole *string
}

type ResourceConfig

ResourceConfig abstracts CPU and memory requirements across providers. Different providers have different granularities and limits: - AWS ECS: CPU in units (256, 512, 1024, etc.), Memory in MB - GCP Cloud Run: CPU in millicores, Memory in MB/GB - Azure Container Instances: CPU cores (0.5, 1, 2, 4), Memory in GB

type ResourceConfig struct {
    // CPU in provider-specific units:
    // - AWS ECS: 256, 512, 1024, 2048, 4096 (CPU units)
    // - GCP Cloud Run: 1000, 2000, 4000 (millicores)
    // - Azure ACI: 1, 2, 4 (CPU cores)
    CPU *int

    // Memory in MB (will be converted to provider-specific units)
    Memory *int
}

type RuntimeConfig

RuntimeConfig specifies platform and architecture requirements. Abstracts provider-specific runtime platform specifications.

type RuntimeConfig struct {
    // Platform specifies the OS and architecture (e.g., "linux/amd64", "linux/arm64")
    // All providers support these standard platform strings
    Platform *string
}

type Service

Service provides the core business logic for command execution and user management.

type Service struct {
    Logger   *slog.Logger
    Provider constants.BackendProvider
    // contains filtered or unexported fields
}

func Initialize

func Initialize(ctx context.Context, cfg *config.Config, baseLogger *slog.Logger) (*Service, error)

Initialize creates a new Service configured for the specified backend provider. It returns an error if the context is canceled, timed out, or if an unknown provider is specified. Callers should handle errors and potentially panic if initialization fails during startup. Also initializes the Casbin enforcer for authorization.

Supported cloud providers:

  • "aws": Uses DynamoDB for storage, Fargate for execution
  • "gcp": (future) E.g. using Google Cloud Run and Firestore for storage

func NewService

func NewService(ctx context.Context, repos *database.Repositories, taskManager contract.TaskManager, imageRegistry contract.ImageRegistry, logManager contract.LogManager, observabilityManager contract.ObservabilityManager, log *slog.Logger, provider constants.BackendProvider, wsManager contract.WebSocketManager, healthManager contract.HealthManager, enforcer *authorization.Enforcer) (*Service, error)

NewService creates a new service instance and initializes the enforcer with user roles from the database. Returns an error if the enforcer is configured but user roles cannot be loaded (critical initialization failure). Core repositories (repos.User, repos.Execution) and enforcer are required for initialization and must be non-nil. If wsManager is nil, WebSocket URL generation will be skipped. If repos.Secrets is nil, secrets operations will not be available. If repos.Image is nil, image-by-request-ID queries will not be available. If healthManager is nil, health reconciliation will not be available.

func (*Service) AuthenticateUser

func (s *Service) AuthenticateUser(ctx context.Context, apiKey string) (*api.User, error)

AuthenticateUser verifies an API key and returns the associated user. Returns appropriate errors for invalid API keys, revoked keys, or server errors.

func (*Service) ClaimAPIKey

func (s *Service) ClaimAPIKey(ctx context.Context, secretToken string, ipAddress string) (*api.ClaimAPIKeyResponse, error)

ClaimAPIKey retrieves and claims a pending API key by its secret token.

func (*Service) CreateSecret

func (s *Service) CreateSecret(ctx context.Context, req *api.CreateSecretRequest, userEmail string) error

CreateSecret creates a new secret with the given name, description, key name, and value.

func (*Service) CreateUser

func (s *Service) CreateUser(ctx context.Context, req api.CreateUserRequest, createdByEmail string) (*api.CreateUserResponse, error)

CreateUser creates a new user with an API key and returns a claim token. If no API key is provided in the request, one will be generated. Requires a valid role to be specified in the request.

func (*Service) DeleteSecret

func (s *Service) DeleteSecret(ctx context.Context, name string) error

DeleteSecret deletes a secret and its value.

func (*Service) FetchBackendLogs

func (s *Service) FetchBackendLogs(ctx context.Context, requestID string) ([]api.LogEvent, error)

FetchBackendLogs retrieves backend infrastructure logs for the provided requestID. Returns logs from backend services for debugging and tracing.

func (*Service) FetchTrace

func (s *Service) FetchTrace(ctx context.Context, requestID string) (*api.TraceResponse, error)

FetchTrace retrieves backend logs and related resources for a request ID.

func (*Service) GetEnforcer

func (s *Service) GetEnforcer() *authorization.Enforcer

GetEnforcer returns the Casbin enforcer for authorization checks.

func (*Service) GetExecutionStatus

func (s *Service) GetExecutionStatus(ctx context.Context, executionID string) (*api.ExecutionStatusResponse, error)

GetExecutionStatus returns the current status and metadata for a given execution ID.

func (*Service) GetImage

func (s *Service) GetImage(ctx context.Context, image string) (*api.ImageInfo, error)

GetImage returns a single registered Docker image by ID or name.

func (*Service) GetLogsByExecutionID

func (s *Service) GetLogsByExecutionID(ctx context.Context, executionID string, userEmail *string, clientIPAtCreationTime *string) (*api.LogsResponse, error)

GetLogsByExecutionID returns aggregated Cloud logs for a given execution. WebSocket endpoint is stored without protocol (normalized in config). Always use wss:// for production WebSocket connections. userEmail: authenticated user email for audit trail. clientIPAtCreationTime: client IP captured when the token was created (for tracing). If task is not running, don't return a WebSocket URL.

func (*Service) GetSecret

func (s *Service) GetSecret(ctx context.Context, name string) (*api.Secret, error)

GetSecret retrieves a secret's metadata and value by name.

func (*Service) ImageRegistry

func (s *Service) ImageRegistry() contract.ImageRegistry

ImageRegistry returns the image management interface.

func (*Service) KillExecution

func (s *Service) KillExecution(ctx context.Context, executionID string) (*api.KillExecutionResponse, error)

KillExecution terminates a running execution identified by executionID. It verifies the execution exists in the database and checks task status before termination. Updates the execution status to TERMINATING after successful task stop.

This operation is idempotent: if the execution is already in a terminal state (SUCCEEDED, FAILED, STOPPED, TERMINATING), it returns nil, nil (which results in HTTP 204 No Content), indicating that no action was taken. If termination is initiated, returns a KillExecutionResponse with the execution ID and a success message.

Returns an error if the execution is not found or termination fails.

func (*Service) ListExecutions

func (s *Service) ListExecutions(ctx context.Context, limit int, statuses []string) ([]*api.Execution, error)

ListExecutions returns executions from the database with optional filtering. Parameters:

  • limit: maximum number of executions to return
  • statuses: optional list of execution statuses to filter by

If statuses is provided, only executions matching one of the specified statuses are returned. Results are returned sorted by started_at in descending order (newest first). Fields with no values are omitted in JSON due to omitempty tags on api.Execution.

func (*Service) ListImages

func (s *Service) ListImages(ctx context.Context) (*api.ListImagesResponse, error)

ListImages returns all registered Docker images.

func (*Service) ListSecrets

func (s *Service) ListSecrets(ctx context.Context) ([]*api.Secret, error)

ListSecrets retrieves all secrets with values.

func (*Service) ListUsers

func (s *Service) ListUsers(ctx context.Context) (*api.ListUsersResponse, error)

ListUsers returns all users in the system sorted by email (excluding API key hashes for security). Returns an error if the query fails. Sorting is delegated to the repository implementation (e.g., DynamoDB GSI).

func (*Service) LogManager

func (s *Service) LogManager() contract.LogManager

LogManager returns the log management interface.

func (*Service) ObservabilityManager

func (s *Service) ObservabilityManager() contract.ObservabilityManager

ObservabilityManager returns the observability interface.

func (*Service) ReconcileResources

func (s *Service) ReconcileResources(ctx context.Context) (*api.HealthReport, error)

ReconcileResources performs health reconciliation for all resources. This method allows synchronous execution via API.

func (*Service) RegisterImage

func (s *Service) RegisterImage(ctx context.Context, req *api.RegisterImageRequest, createdBy string) (*api.RegisterImageResponse, error)

RegisterImage registers a Docker image and creates the corresponding task definition. After successful registration, ownership is synced to the Casbin enforcer to maintain consistency with the database. If ownership sync fails, an error is logged but registration succeeds; ownership will be synced during the next hydration cycle or health reconcile.

func (*Service) RemoveImage

func (s *Service) RemoveImage(ctx context.Context, image string) error

RemoveImage removes a Docker image and deregisters its task definitions.

func (*Service) ResolveImage

func (s *Service) ResolveImage(ctx context.Context, image string) (*api.ImageInfo, error)

ResolveImage resolves a user-provided image string to a specific ImageInfo. If image string is empty, returns the default image. This centralizes image resolution logic for authorization and execution.

func (*Service) RevokeUser

func (s *Service) RevokeUser(ctx context.Context, email string) error

RevokeUser marks a user's API key as revoked. Returns an error if the user does not exist or revocation fails.

func (*Service) RunCommand

func (s *Service) RunCommand(ctx context.Context, userEmail string, req *api.ExecutionRequest, resolvedImage *api.ImageInfo) (*api.ExecutionResponse, error)

RunCommand starts a provider-specific task and records the execution. The resolvedImage parameter contains the validated image that will be used for execution. The request's Image field is replaced with the imageID before passing to the runner. Secret references are resolved to environment variables before starting the task. Execution status is set to STARTING after the task has been accepted by the provider.

func (*Service) TaskManager

func (s *Service) TaskManager() contract.TaskManager

TaskManager returns the task execution interface.

func (*Service) UpdateSecret

func (s *Service) UpdateSecret(ctx context.Context, name string, req *api.UpdateSecretRequest, userEmail string) error

UpdateSecret updates a secret (metadata and/or value).

func (*Service) UpdateUserLastUsed

func (s *Service) UpdateUserLastUsed(ctx context.Context, email string) (*time.Time, error)

UpdateUserLastUsed updates the user's last_used timestamp after successful authentication. This is a best-effort operation; callers may choose to log failures without failing the request.

func (*Service) ValidateExecutionResourceAccess

func (s *Service) ValidateExecutionResourceAccess(ctx context.Context, userEmail string, req *api.ExecutionRequest, resolvedImage *api.ImageInfo) error

ValidateExecutionResourceAccess checks if a user can access all resources required for execution. The resolvedImage parameter contains the image that was resolved from the request and will be validated. All secrets referenced in the execution request are also validated for access. Returns an error if the user lacks access to any required resource.

processor

import "runvoy/internal/backend/processor"

Package processor provides event processing interfaces and utilities.

Package processor provides event processing functionality for cloud provider events. It handles asynchronous events from AWS services like EventBridge, CloudWatch Logs, and WebSocket lifecycle events.

Index

type Processor

Processor defines the interface for event processing across different cloud providers. Each provider implements this interface to handle events specific to their platform.

type Processor interface {
    // Handle processes a raw cloud event and returns a result (or nil for non-WebSocket events).
    // For WebSocket events, the result is a provider-specific response marshaled as JSON.
    Handle(ctx context.Context, rawEvent *json.RawMessage) (*json.RawMessage, error)

    // HandleEventJSON is a helper for testing that accepts raw JSON and returns an error.
    // It's used for test cases that expect error returns.
    HandleEventJSON(ctx context.Context, eventJSON *json.RawMessage) error
}

func Initialize

func Initialize(ctx context.Context, cfg *config.Config, logger *slog.Logger) (Processor, error)

Initialize creates a new Processor configured for the backend provider specified in cfg. It returns an error if the context is canceled, timed out, or if an unknown provider is specified. Callers should handle errors and potentially panic if initialization fails during startup. Also initializes the Casbin enforcer for authorization.

Supported cloud providers:

  • "aws": Uses CloudWatch events for ECS task state changes and API Gateway for WebSocket events
  • "gcp": (future) Google Cloud Pub/Sub and Cloud Tasks for event processing

websocket

import "runvoy/internal/backend/websocket"

Package websocket provides WebSocket management for runvoy. It handles connection lifecycle events and manages WebSocket connections.

Index

type Manager

Manager exposes the subset of WebSocket manager functionality used by the event processor. Different cloud providers can implement this interface to support their specific WebSocket infrastructure.

type Manager interface {
    // HandleRequest processes WebSocket lifecycle events (connect, disconnect, etc.).
    // Returns true if the event was handled, false otherwise.
    HandleRequest(ctx context.Context, rawEvent *json.RawMessage, reqLogger *slog.Logger) (bool, error)

    // NotifyExecutionCompletion sends disconnect notifications to all connected clients for an execution
    // and removes the connections.
    NotifyExecutionCompletion(ctx context.Context, executionID *string) error

    // SendLogsToExecution sends log events to all connected clients for an execution.
    SendLogsToExecution(ctx context.Context, executionID *string, logEvents []api.LogEvent) error

    // GenerateWebSocketURL creates a WebSocket token and returns the connection URL.
    // It stores the token for validation when the client connects.
    // Returns an empty string if URL generation fails (errors are logged).
    GenerateWebSocketURL(
        ctx context.Context,
        executionID string,
        userEmail *string,
        clientIPAtCreationTime *string,
    ) string
}

infra

import "runvoy/internal/client/infra"

Package infra provides infrastructure deployment functionality.

Package infra provides infrastructure deployment functionality.

Index

func BuildLogsURL

func BuildLogsURL(webURL, executionID string) string

BuildLogsURL constructs a properly formatted URL for viewing logs in the web viewer. It takes a base web URL and an execution ID, and returns a URL string with the /logs path and execution_id query parameter properly encoded.

If URL parsing or path joining fails, it falls back to simple string concatenation to ensure a URL is always returned (though it may not be perfectly formatted).

func ParseParameters

func ParseParameters(params []string) (map[string]string, error)

ParseParameters parses KEY=VALUE parameter strings

func SeedAdminUser

func SeedAdminUser(ctx context.Context, adminEmail, region, tableName string) (string, error)

SeedAdminUser seeds an admin user into the database and returns the generated API key. This function hides the DynamoDB implementation details from callers.

type AWSDeployer

AWSDeployer implements Deployer for AWS CloudFormation

type AWSDeployer struct {
    // contains filtered or unexported fields
}

func NewAWSDeployer

func NewAWSDeployer(ctx context.Context, region string) (*AWSDeployer, error)

NewAWSDeployer creates a new AWS deployer with the given region. If region is empty, uses the AWS SDK default.

func NewAWSDeployerWithClient

func NewAWSDeployerWithClient(client CloudFormationClient, region string) *AWSDeployer

NewAWSDeployerWithClient creates a new AWS deployer with a custom client (for testing)

func (*AWSDeployer) CheckStackExists

func (d *AWSDeployer) CheckStackExists(ctx context.Context, stackName string) (bool, error)

CheckStackExists checks if a CloudFormation stack exists

func (*AWSDeployer) Deploy

func (d *AWSDeployer) Deploy(ctx context.Context, opts *DeployOptions) (*DeployResult, error)

Deploy deploys or updates the CloudFormation stack

func (*AWSDeployer) Destroy

func (d *AWSDeployer) Destroy(ctx context.Context, opts *DestroyOptions) (*DestroyResult, error)

Destroy destroys the CloudFormation stack

func (*AWSDeployer) GetRegion

func (d *AWSDeployer) GetRegion() string

GetRegion returns the AWS region being used

func (*AWSDeployer) GetStackOutputs

func (d *AWSDeployer) GetStackOutputs(ctx context.Context, stackName string) (map[string]string, error)

GetStackOutputs retrieves the outputs from a CloudFormation stack

type CloudFormationClient

CloudFormationClient defines the interface for CloudFormation operations. This interface enables mocking for unit tests.

type CloudFormationClient interface {
    DescribeStacks(
        ctx context.Context,
        params *cloudformation.DescribeStacksInput,
        optFns ...func(*cloudformation.Options),
    ) (*cloudformation.DescribeStacksOutput, error)
    DescribeStackEvents(
        ctx context.Context,
        params *cloudformation.DescribeStackEventsInput,
        optFns ...func(*cloudformation.Options),
    ) (*cloudformation.DescribeStackEventsOutput, error)
    CreateStack(
        ctx context.Context,
        params *cloudformation.CreateStackInput,
        optFns ...func(*cloudformation.Options),
    ) (*cloudformation.CreateStackOutput, error)
    UpdateStack(
        ctx context.Context,
        params *cloudformation.UpdateStackInput,
        optFns ...func(*cloudformation.Options),
    ) (*cloudformation.UpdateStackOutput, error)
    DeleteStack(
        ctx context.Context,
        params *cloudformation.DeleteStackInput,
        optFns ...func(*cloudformation.Options),
    ) (*cloudformation.DeleteStackOutput, error)
}

type DeployOptions

DeployOptions contains all options for deploying infrastructure

type DeployOptions struct {
    StackName  string
    Template   string   // URL, S3 URI, or local file path
    Version    string   // Release version
    Parameters []string // KEY=VALUE format
    Wait       bool     // Wait for completion
    Region     string   // Provider region (optional)
}

type DeployResult

DeployResult contains the result of a deployment operation

type DeployResult struct {
    StackName     string
    OperationType string // "CREATE" or "UPDATE"
    Status        string
    Outputs       map[string]string
    NoChanges     bool // True if stack was already up to date
}

type Deployer

Deployer defines the interface for infrastructure deployment. Different cloud providers implement this interface.

type Deployer interface {
    // Deploy deploys or updates infrastructure
    Deploy(ctx context.Context, opts *DeployOptions) (*DeployResult, error)
    // Destroy destroys infrastructure
    Destroy(ctx context.Context, opts *DestroyOptions) (*DestroyResult, error)
    // CheckStackExists checks if the infrastructure stack exists
    CheckStackExists(ctx context.Context, stackName string) (bool, error)
    // GetStackOutputs retrieves outputs from a deployed stack
    GetStackOutputs(ctx context.Context, stackName string) (map[string]string, error)
    // GetRegion returns the region being used
    GetRegion() string
}

func NewDeployer

func NewDeployer(ctx context.Context, provider, region string) (Deployer, error)

NewDeployer creates a Deployer for the specified provider. Currently supports: "aws"

type DestroyOptions

DestroyOptions contains all options for destroying infrastructure

type DestroyOptions struct {
    StackName string
    Wait      bool   // Wait for completion
    Region    string // Provider region (optional)
}

type DestroyResult

DestroyResult contains the result of a destroy operation

type DestroyResult struct {
    StackName string
    Status    string
    NotFound  bool // True if stack was already deleted
}

type TemplateSource

TemplateSource represents the resolved template source

type TemplateSource struct {
    URL  string // For remote templates (S3/HTTPS)
    Body string // For local file templates
}

func ResolveTemplate

func ResolveTemplate(provider, template, version, region string) (*TemplateSource, error)

ResolveTemplate determines the template source from the given input. Returns a TemplateSource with either URL or Body populated. region is the provider region to use for building default template URLs.

output

import "runvoy/internal/client/output"

Package output provides formatted terminal output utilities. It includes colors, spinners, progress bars, and other CLI display helpers.

Index

Variables

var (

    // Stdout is the output writer for normal output (can be overridden for testing).
    Stdout io.Writer = os.Stdout
    // Stderr is the output writer for error output (can be overridden for testing).
    Stderr io.Writer = os.Stderr
)

func Blank

func Blank()

Blank prints a blank line

func Bold

func Bold(text string) string

Bold prints text in bold

func Box

func Box(text string)

Box prints text in a rounded box (to stderr) Example: ╭─────────────────────────────╮ │ Configuration saved! │ ╰─────────────────────────────╯

func Bytes

func Bytes(b int64) string

Bytes formats bytes in a human-readable way

func Confirm

func Confirm(prompt string) bool

Confirm prompts the user for yes/no confirmation Returns true if user confirms (y/Y), false otherwise

func Cyan

func Cyan(text string) string

Cyan prints text in cyan

func Duration

func Duration(d time.Duration) string

Duration formats a duration in a human-readable way

func Errorf

func Errorf(format string, a ...any)

Errorf prints an error message with an X symbol (to stderr) Example: ✗ Failed to create stack: permission denied

func Fatalf

func Fatalf(format string, a ...any)

Fatalf prints an error message and exits with code 1

func Gray

func Gray(text string) string

Gray prints text in gray

func Green

func Green(text string) string

Green prints text in green

func Header

func Header(text string)

Header prints a section header with a separator line (to stderr) Example: 🚀 Initializing runvoy infrastructure ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

func Infof

func Infof(format string, a ...any)

Infof prints an informational message with an arrow (to stderr) Example: → Creating CloudFormation stack...

func KeyValue

func KeyValue(key, value string)

KeyValue prints a key-value pair with indentation Example: Stack name: runvoy

func KeyValueBold

func KeyValueBold(key, value string)

KeyValueBold prints a key-value pair with bold value Example: API Key: sk_live_abc123...

func List

func List(items []string)

List prints a bulleted list Example:

  • Item one
  • Item two
  • Item three

func NumberedList

func NumberedList(items []string)

NumberedList prints a numbered list Example:

  1. First step
  2. Second step
  3. Third step

func Printf

func Printf(format string, a ...any)

Printf prints a formatted plain line

func Println

func Println(a ...any)

Println prints a plain line without any formatting

func Prompt

func Prompt(prompt string) string

Prompt prompts the user for input

func PromptRequired

func PromptRequired(prompt string) string

PromptRequired prompts the user for input and requires a non-empty response

func PromptSecret

func PromptSecret(prompt string) string

PromptSecret prompts for sensitive input (like passwords) Note: This is a simple implementation. For production, consider using golang.org/x/term for proper terminal handling

func Red

func Red(text string) string

Red prints text in red

func StatusBadge

func StatusBadge(status string) string

StatusBadge prints a colored status badge

func Step

func Step(step, total int, message string)

Step prints a step in a multi-step process (to stderr) Example: 1/3 Waiting for stack creation

func StepError

func StepError(step, total int, message string)

StepError prints a failed step (to stderr) Example: 2/3 ✗ Failed to generate API key

func StepSuccess

func StepSuccess(step, total int, message string)

StepSuccess prints a successful step completion (to stderr) Example: 1/3 ✓ Stack created

func Subheader

func Subheader(text string)

Subheader prints a smaller section header (to stderr) Example: Configuration Details ────────────────────

func Successf

func Successf(format string, a ...any)

Successf prints a success message with a checkmark (to stderr) Example: ✓ Stack created successfully

func Table

func Table(headers []string, rows [][]string)

Table prints a simple table with headers Example: Execution ID Status Duration ──────────── ────── ──────── exec_abc123 completed 10m 35s exec_def456 running 2m 15s

func Warningf

func Warningf(format string, a ...any)

Warningf prints a warning message with a warning symbol (to stderr) Example: ⚠ Lock already held by alice@acme.com

func Yellow

func Yellow(text string) string

Yellow prints text in yellow

type ProgressBar

ProgressBar represents a simple text progress bar

type ProgressBar struct {
    // contains filtered or unexported fields
}

func NewProgressBar

func NewProgressBar(total int, message string) *ProgressBar

NewProgressBar creates a new progress bar

func (*ProgressBar) Complete

func (p *ProgressBar) Complete()

Complete marks the progress bar as complete

func (*ProgressBar) Increment

func (p *ProgressBar) Increment()

Increment increments the progress bar by 1

func (*ProgressBar) Update

func (p *ProgressBar) Update(current int)

Update updates the progress bar to the given value (to stderr)

type Spinner

Spinner represents a simple text spinner for long operations

type Spinner struct {
    // contains filtered or unexported fields
}

func NewSpinner

func NewSpinner(message string) *Spinner

NewSpinner creates a new spinner with the given message

func (*Spinner) Error

func (s *Spinner) Error(message string)

Error stops the spinner and prints an error message

func (*Spinner) Start

func (s *Spinner) Start()

Start starts the spinner animation (to stderr)

func (*Spinner) Stop

func (s *Spinner) Stop()

Stop stops the spinner and clears the line

func (*Spinner) Success

func (s *Spinner) Success(message string)

Success stops the spinner and prints a success message

playbooks

import "runvoy/internal/client/playbooks"

Package playbooks provides functionality for loading and managing playbooks.

Package playbooks provides functionality for loading and managing playbooks.

Index

type PlaybookExecutor

PlaybookExecutor converts Playbook to ExecutionRequest

type PlaybookExecutor struct{}

func NewPlaybookExecutor

func NewPlaybookExecutor() *PlaybookExecutor

NewPlaybookExecutor creates a new PlaybookExecutor

func (*PlaybookExecutor) ToExecutionRequest

func (e *PlaybookExecutor) ToExecutionRequest(playbook *api.Playbook, userEnv map[string]string, userSecrets []string) *api.ExecutionRequest

ToExecutionRequest converts a Playbook to an ExecutionRequest. Combines multiple commands with && operator and merges env vars and secrets.

type PlaybookLoader

PlaybookLoader handles loading and discovery of playbooks

type PlaybookLoader struct{}

func NewPlaybookLoader

func NewPlaybookLoader() *PlaybookLoader

NewPlaybookLoader creates a new PlaybookLoader

func (*PlaybookLoader) GetPlaybookDir

func (l *PlaybookLoader) GetPlaybookDir() (string, error)

GetPlaybookDir returns the path to the playbook directory. Checks current working directory first, falls back to home directory.

func (*PlaybookLoader) ListPlaybooks

func (l *PlaybookLoader) ListPlaybooks() ([]string, error)

ListPlaybooks scans the playbook directory for YAML files and returns playbook names. Returns empty list if directory doesn't exist.

func (*PlaybookLoader) LoadPlaybook

func (l *PlaybookLoader) LoadPlaybook(name string) (*api.Playbook, error)

LoadPlaybook loads and parses a playbook YAML file.

aws

import "runvoy/internal/config/aws"

Package aws contains AWS-specific configuration helpers for Runvoy services.

Index

func BindEnvVars

func BindEnvVars(v *viper.Viper)

BindEnvVars binds AWS-specific environment variables to the provided Viper instance.

func BuildTemplateURL

func BuildTemplateURL(version, region string) string

BuildTemplateURL builds the S3 HTTPS URL for the CloudFormation template. The version is normalized to remove any 'v' prefix before building the URL. If region is empty, defaults to the ReleasesBucketRegion constant.

func NormalizeVersion

func NormalizeVersion(version string) string

NormalizeVersion strips any 'v' prefix from the version string. S3 paths use versions without the 'v' prefix (e.g., "0.1.0" not "v0.1.0").

func NormalizeWebSocketEndpoint

func NormalizeWebSocketEndpoint(endpoint string) string

NormalizeWebSocketEndpoint strips protocol prefixes from WebSocket endpoint URLs. Accepts: https://example.com, http://example.com, wss://example.com, ws://example.com, example.com Returns: example.com (without protocol)

func ValidateEventProcessor

func ValidateEventProcessor(cfg *Config) error

ValidateEventProcessor validates required AWS fields for the event processor service.

func ValidateOrchestrator

func ValidateOrchestrator(cfg *Config) error

ValidateOrchestrator validates required AWS fields for the orchestrator service.

type Config

Config contains AWS-specific configuration. These settings are only used when the backend provider is AWS.

type Config struct {
    // DynamoDB Tables
    APIKeysTable              string `mapstructure:"api_keys_table"`
    ExecutionsTable           string `mapstructure:"executions_table"`
    ImageTaskDefsTable        string `mapstructure:"image_taskdefs_table"`
    PendingAPIKeysTable       string `mapstructure:"pending_api_keys_table"`
    SecretsMetadataTable      string `mapstructure:"secrets_metadata_table"`
    WebSocketConnectionsTable string `mapstructure:"websocket_connections_table"`
    WebSocketTokensTable      string `mapstructure:"websocket_tokens_table"`

    // ECS Configuration
    DefaultTaskExecRoleARN string `mapstructure:"default_task_exec_role_arn"`
    DefaultTaskRoleARN     string `mapstructure:"default_task_role_arn"`
    ECSCluster             string `mapstructure:"ecs_cluster"`
    SecurityGroup          string `mapstructure:"security_group"`
    Subnet1                string `mapstructure:"subnet_1"`
    Subnet2                string `mapstructure:"subnet_2"`
    TaskDefinition         string `mapstructure:"task_definition"`

    // CloudWatch Logs
    LogGroup string `mapstructure:"log_group"`

    // API Gateway WebSocket
    WebSocketAPIEndpoint string `mapstructure:"websocket_api_endpoint"`

    // Secrets Management
    SecretsPrefix    string `mapstructure:"secrets_prefix"`
    SecretsKMSKeyARN string `mapstructure:"secrets_kms_key_arn"`

    // Infrastructure defaults
    InfraDefaultStackName string `mapstructure:"infra_default_stack_name" yaml:"infra_default_stack_name"`

    // AWS SDK Configuration (credentials, region, etc.)
    SDKConfig *aws.Config `mapstructure:"-"`
}

func (*Config) LoadSDKConfig

func (c *Config) LoadSDKConfig(ctx context.Context) error

LoadSDKConfig loads the AWS SDK configuration from the environment.

client

import "runvoy/internal/providers/aws/client"

Package client provides AWS-specific client interfaces for AWS services. This package contains the interfaces for the AWS clients used by the Runvoy orchestrator.

Index

type CloudWatchLogsClient

CloudWatchLogsClient defines the interface for CloudWatch Logs operations used by the runner. This interface makes the code easier to test by allowing mock implementations.

type CloudWatchLogsClient interface {
    DescribeLogGroups(
        ctx context.Context,
        params *cloudwatchlogs.DescribeLogGroupsInput,
        optFns ...func(*cloudwatchlogs.Options),
    ) (*cloudwatchlogs.DescribeLogGroupsOutput, error)
    DescribeLogStreams(
        ctx context.Context,
        params *cloudwatchlogs.DescribeLogStreamsInput,
        optFns ...func(*cloudwatchlogs.Options),
    ) (*cloudwatchlogs.DescribeLogStreamsOutput, error)
    GetLogEvents(
        ctx context.Context,
        params *cloudwatchlogs.GetLogEventsInput,
        optFns ...func(*cloudwatchlogs.Options),
    ) (*cloudwatchlogs.GetLogEventsOutput, error)
    StartQuery(
        ctx context.Context,
        params *cloudwatchlogs.StartQueryInput,
        optFns ...func(*cloudwatchlogs.Options),
    ) (*cloudwatchlogs.StartQueryOutput, error)
    GetQueryResults(
        ctx context.Context,
        params *cloudwatchlogs.GetQueryResultsInput,
        optFns ...func(*cloudwatchlogs.Options),
    ) (*cloudwatchlogs.GetQueryResultsOutput, error)
}

type CloudWatchLogsClientAdapter

CloudWatchLogsClientAdapter wraps the AWS SDK CloudWatch Logs client to implement CloudWatchLogsClient interface. This allows us to use the real AWS client in production while maintaining testability.

type CloudWatchLogsClientAdapter struct {
    // contains filtered or unexported fields
}

func NewCloudWatchLogsClientAdapter

func NewCloudWatchLogsClientAdapter(client *cloudwatchlogs.Client) *CloudWatchLogsClientAdapter

NewCloudWatchLogsClientAdapter creates a new adapter wrapping the AWS SDK CloudWatch Logs client.

func (*CloudWatchLogsClientAdapter) DescribeLogGroups

func (a *CloudWatchLogsClientAdapter) DescribeLogGroups(ctx context.Context, params *cloudwatchlogs.DescribeLogGroupsInput, optFns ...func(*cloudwatchlogs.Options)) (*cloudwatchlogs.DescribeLogGroupsOutput, error)

DescribeLogGroups wraps the AWS SDK DescribeLogGroups operation.

func (*CloudWatchLogsClientAdapter) DescribeLogStreams

func (a *CloudWatchLogsClientAdapter) DescribeLogStreams(ctx context.Context, params *cloudwatchlogs.DescribeLogStreamsInput, optFns ...func(*cloudwatchlogs.Options)) (*cloudwatchlogs.DescribeLogStreamsOutput, error)

DescribeLogStreams wraps the AWS SDK DescribeLogStreams operation.

func (*CloudWatchLogsClientAdapter) GetLogEvents

func (a *CloudWatchLogsClientAdapter) GetLogEvents(ctx context.Context, params *cloudwatchlogs.GetLogEventsInput, optFns ...func(*cloudwatchlogs.Options)) (*cloudwatchlogs.GetLogEventsOutput, error)

GetLogEvents wraps the AWS SDK GetLogEvents operation.

func (*CloudWatchLogsClientAdapter) GetQueryResults

func (a *CloudWatchLogsClientAdapter) GetQueryResults(ctx context.Context, params *cloudwatchlogs.GetQueryResultsInput, optFns ...func(*cloudwatchlogs.Options)) (*cloudwatchlogs.GetQueryResultsOutput, error)

GetQueryResults wraps the AWS SDK GetQueryResults operation for CloudWatch Logs Insights.

func (*CloudWatchLogsClientAdapter) StartQuery

func (a *CloudWatchLogsClientAdapter) StartQuery(ctx context.Context, params *cloudwatchlogs.StartQueryInput, optFns ...func(*cloudwatchlogs.Options)) (*cloudwatchlogs.StartQueryOutput, error)

StartQuery wraps the AWS SDK StartQuery operation for CloudWatch Logs Insights.

type ECSClient

ECSClient defines the interface for ECS operations used across AWS provider packages. This interface makes the code easier to test by allowing mock implementations.

type ECSClient interface {
    RunTask(
        ctx context.Context,
        params *ecs.RunTaskInput,
        optFns ...func(*ecs.Options),
    ) (*ecs.RunTaskOutput, error)
    TagResource(
        ctx context.Context,
        params *ecs.TagResourceInput,
        optFns ...func(*ecs.Options),
    ) (*ecs.TagResourceOutput, error)
    ListTasks(
        ctx context.Context,
        params *ecs.ListTasksInput,
        optFns ...func(*ecs.Options),
    ) (*ecs.ListTasksOutput, error)
    DescribeTasks(
        ctx context.Context,
        params *ecs.DescribeTasksInput,
        optFns ...func(*ecs.Options),
    ) (*ecs.DescribeTasksOutput, error)
    StopTask(
        ctx context.Context,
        params *ecs.StopTaskInput,
        optFns ...func(*ecs.Options),
    ) (*ecs.StopTaskOutput, error)
    DescribeTaskDefinition(
        ctx context.Context,
        params *ecs.DescribeTaskDefinitionInput,
        optFns ...func(*ecs.Options),
    ) (*ecs.DescribeTaskDefinitionOutput, error)
    ListTagsForResource(
        ctx context.Context,
        params *ecs.ListTagsForResourceInput,
        optFns ...func(*ecs.Options),
    ) (*ecs.ListTagsForResourceOutput, error)
    ListTaskDefinitions(
        ctx context.Context,
        params *ecs.ListTaskDefinitionsInput,
        optFns ...func(*ecs.Options),
    ) (*ecs.ListTaskDefinitionsOutput, error)
    RegisterTaskDefinition(
        ctx context.Context,
        params *ecs.RegisterTaskDefinitionInput,
        optFns ...func(*ecs.Options),
    ) (*ecs.RegisterTaskDefinitionOutput, error)
    DeregisterTaskDefinition(
        ctx context.Context,
        params *ecs.DeregisterTaskDefinitionInput,
        optFns ...func(*ecs.Options),
    ) (*ecs.DeregisterTaskDefinitionOutput, error)
    DeleteTaskDefinitions(
        ctx context.Context,
        params *ecs.DeleteTaskDefinitionsInput,
        optFns ...func(*ecs.Options),
    ) (*ecs.DeleteTaskDefinitionsOutput, error)
    UntagResource(
        ctx context.Context,
        params *ecs.UntagResourceInput,
        optFns ...func(*ecs.Options),
    ) (*ecs.UntagResourceOutput, error)
}

type ECSClientAdapter

ECSClientAdapter wraps the AWS SDK ECS client to implement ECSClient interface. This allows us to use the real AWS client in production while maintaining testability.

type ECSClientAdapter struct {
    // contains filtered or unexported fields
}

func NewECSClientAdapter

func NewECSClientAdapter(client *ecs.Client) *ECSClientAdapter

NewECSClientAdapter creates a new adapter wrapping the AWS SDK ECS client.

func (*ECSClientAdapter) DeleteTaskDefinitions

func (a *ECSClientAdapter) DeleteTaskDefinitions(ctx context.Context, params *ecs.DeleteTaskDefinitionsInput, optFns ...func(*ecs.Options)) (*ecs.DeleteTaskDefinitionsOutput, error)

DeleteTaskDefinitions wraps the AWS SDK DeleteTaskDefinitions operation.

func (*ECSClientAdapter) DeregisterTaskDefinition

func (a *ECSClientAdapter) DeregisterTaskDefinition(ctx context.Context, params *ecs.DeregisterTaskDefinitionInput, optFns ...func(*ecs.Options)) (*ecs.DeregisterTaskDefinitionOutput, error)

DeregisterTaskDefinition wraps the AWS SDK DeregisterTaskDefinition operation.

func (*ECSClientAdapter) DescribeTaskDefinition

func (a *ECSClientAdapter) DescribeTaskDefinition(ctx context.Context, params *ecs.DescribeTaskDefinitionInput, optFns ...func(*ecs.Options)) (*ecs.DescribeTaskDefinitionOutput, error)

DescribeTaskDefinition wraps the AWS SDK DescribeTaskDefinition operation.

func (*ECSClientAdapter) DescribeTasks

func (a *ECSClientAdapter) DescribeTasks(ctx context.Context, params *ecs.DescribeTasksInput, optFns ...func(*ecs.Options)) (*ecs.DescribeTasksOutput, error)

DescribeTasks wraps the AWS SDK DescribeTasks operation.

func (*ECSClientAdapter) ListTagsForResource

func (a *ECSClientAdapter) ListTagsForResource(ctx context.Context, params *ecs.ListTagsForResourceInput, optFns ...func(*ecs.Options)) (*ecs.ListTagsForResourceOutput, error)

ListTagsForResource wraps the AWS SDK ListTagsForResource operation.

func (*ECSClientAdapter) ListTaskDefinitions

func (a *ECSClientAdapter) ListTaskDefinitions(ctx context.Context, params *ecs.ListTaskDefinitionsInput, optFns ...func(*ecs.Options)) (*ecs.ListTaskDefinitionsOutput, error)

ListTaskDefinitions wraps the AWS SDK ListTaskDefinitions operation.

func (*ECSClientAdapter) ListTasks

func (a *ECSClientAdapter) ListTasks(ctx context.Context, params *ecs.ListTasksInput, optFns ...func(*ecs.Options)) (*ecs.ListTasksOutput, error)

ListTasks wraps the AWS SDK ListTasks operation.

func (*ECSClientAdapter) RegisterTaskDefinition

func (a *ECSClientAdapter) RegisterTaskDefinition(ctx context.Context, params *ecs.RegisterTaskDefinitionInput, optFns ...func(*ecs.Options)) (*ecs.RegisterTaskDefinitionOutput, error)

RegisterTaskDefinition wraps the AWS SDK RegisterTaskDefinition operation.

func (*ECSClientAdapter) RunTask

func (a *ECSClientAdapter) RunTask(ctx context.Context, params *ecs.RunTaskInput, optFns ...func(*ecs.Options)) (*ecs.RunTaskOutput, error)

RunTask wraps the AWS SDK RunTask operation.

func (*ECSClientAdapter) StopTask

func (a *ECSClientAdapter) StopTask(ctx context.Context, params *ecs.StopTaskInput, optFns ...func(*ecs.Options)) (*ecs.StopTaskOutput, error)

StopTask wraps the AWS SDK StopTask operation.

func (*ECSClientAdapter) TagResource

func (a *ECSClientAdapter) TagResource(ctx context.Context, params *ecs.TagResourceInput, optFns ...func(*ecs.Options)) (*ecs.TagResourceOutput, error)

TagResource wraps the AWS SDK TagResource operation.

func (*ECSClientAdapter) UntagResource

func (a *ECSClientAdapter) UntagResource(ctx context.Context, params *ecs.UntagResourceInput, optFns ...func(*ecs.Options)) (*ecs.UntagResourceOutput, error)

UntagResource wraps the AWS SDK UntagResource operation.

type IAMClient

IAMClient defines the interface for IAM operations used across AWS provider packages. This interface makes the code easier to test by allowing mock implementations.

type IAMClient interface {
    GetRole(
        ctx context.Context,
        params *iam.GetRoleInput,
        optFns ...func(*iam.Options),
    ) (*iam.GetRoleOutput, error)
}

type IAMClientAdapter

IAMClientAdapter wraps the AWS SDK IAM client to implement IAMClient interface. This allows us to use the real AWS client in production while maintaining testability.

type IAMClientAdapter struct {
    // contains filtered or unexported fields
}

func NewIAMClientAdapter

func NewIAMClientAdapter(client *iam.Client) *IAMClientAdapter

NewIAMClientAdapter creates a new adapter wrapping the AWS SDK IAM client.

func (*IAMClientAdapter) GetRole

func (a *IAMClientAdapter) GetRole(ctx context.Context, params *iam.GetRoleInput, optFns ...func(*iam.Options)) (*iam.GetRoleOutput, error)

GetRole wraps the AWS SDK GetRole operation.

constants

import "runvoy/internal/providers/aws/constants"

Package constants provides AWS-specific constants for CloudWatch logging and events.

Package constants provides AWS-specific constants for ECS task execution.

Package constants provides AWS-specific constants for infrastructure deployment.

Package constants provides AWS-specific constants and utilities for log stream handling.

Package constants provides AWS-specific constants for secrets management.

Index

Constants

const (
    // DefaultInfraStackName is the default CloudFormation stack name for AWS infra deployments
    DefaultInfraStackName = "runvoy-backend"

    // ReleasesBucketRegion is the AWS region where the releases bucket is located
    ReleasesBucketRegion = "us-east-1"

    // ReleasesBucket is the S3 bucket name for runvoy releases
    ReleasesBucket = "runvoy-releases-" + ReleasesBucketRegion

    // CloudFormationTemplateFile is the filename of the CloudFormation template in releases
    CloudFormationTemplateFile = "cloudformation-backend.yaml"
)

CloudWatchLogsDescribeLimit is the limit for CloudWatch Logs DescribeLogStreams API

const CloudWatchLogsDescribeLimit = int32(50)

CloudWatchLogsEventsLimit is the limit for CloudWatch Logs GetLogEvents API

const CloudWatchLogsEventsLimit = int32(10000)

CloudWatchLogsQueryInitialDelay is the initial delay in seconds to allow CloudWatch Logs Insights query to become ready before polling

const CloudWatchLogsQueryInitialDelay = 10 * time.Second

CloudWatchLogsQueryMaxAttempts is the maximum number of polling attempts for CloudWatch Logs Insights query results

const CloudWatchLogsQueryMaxAttempts = 30

CloudWatchLogsQueryPollInterval is the polling interval in milliseconds for checking CloudWatch Logs Insights query results

const CloudWatchLogsQueryPollInterval = time.Second

DefaultCPU is the default CPU units for ECS task definitions

const DefaultCPU = 256

DefaultExecutionListCapacity is the initial slice capacity used when listing executions from DynamoDB without an explicit limit.

const DefaultExecutionListCapacity = 16

DefaultMemory is the default memory (in MB) for ECS task definitions

const DefaultMemory = 512

DefaultRuntimePlatform is the default runtime platform for ECS task definitions

const DefaultRuntimePlatform = DefaultRuntimePlatformOSFamily + "/" + DefaultRuntimePlatformArchitecture

DefaultRuntimePlatformArchitecture is the default architecture for ECS task definitions

const DefaultRuntimePlatformArchitecture = RuntimePlatformArchARM64

DefaultRuntimePlatformOSFamily is the default OS family for ECS task definitions

const DefaultRuntimePlatformOSFamily = "Linux"

ECSEphemeralStorageSizeGiB is the ECS ephemeral storage size in GiB

const ECSEphemeralStorageSizeGiB = 21

ECSTaskDefinitionMaxResults is the maximum number of results for ECS ListTaskDefinitions

const ECSTaskDefinitionMaxResults = int32(100)

LogGroupPrefix is the prefix for all runvoy Lambda log groups

const LogGroupPrefix = "/aws/lambda/" + constants.ProjectName

LogStreamPartsCount is the expected number of parts in a log stream name Format: task/{container}/{execution_id} = 3 parts

const LogStreamPartsCount = 3

LogStreamPrefix is the prefix for all log stream names for ECS tasks

const LogStreamPrefix = "task"

RunnerContainerName is the ECS container name used for task execution. Must match the container override name passed in the ECS RunTask call.

const RunnerContainerName = "runner"

RuntimePlatformArchARM64 is the ARM64 architecture for ECS runtime platform

const RuntimePlatformArchARM64 = "ARM64"

RuntimePlatformArchX8664 is the x86_64 architecture for ECS runtime platform

const RuntimePlatformArchX8664 = "X86_64"

SSMParameterMaxResults is the maximum number of results for SSM DescribeParameters

const SSMParameterMaxResults = int32(50)

ScheduledEventHealthReconcile is the expected runvoy_event payload value for EventBridge scheduled events that trigger health reconciliation.

const ScheduledEventHealthReconcile = "health_reconcile"

SecretsPrefix is the prefix for AWS secrets management.

const SecretsPrefix = "/runvoy/secrets" //nolint:gosec // G101: This is a constant, not a hardcoded credential

SharedVolumeName is the name of the shared volume between containers. Used for sharing the cloned git repository from sidecar to main container.

const SharedVolumeName = "workspace"

SharedVolumePath is the mount path for the shared volume in both containers.

const SharedVolumePath = "/workspace"

SidecarContainerName is the sidecar container name for auxiliary tasks. This container runs before the main runner container and handles tasks like .env file generation from user environment variables, git repository cloning, etc.

const SidecarContainerName = "sidecar"

TaskDefinitionDockerImageTagKey is the ECS tag key used to store the Docker image name for metadata

const TaskDefinitionDockerImageTagKey = "DockerImage"

TaskDefinitionFamilyPrefix is the prefix for all runvoy task definition families Task definitions are named: {ProjectName}-image-{sanitized-image-name} e.g., "runvoy-image-hashicorp-terraform-1-6" for image "hashicorp/terraform:1.6"

const TaskDefinitionFamilyPrefix = constants.ProjectName + "-image"

TaskDefinitionIsDefaultTagKey is the ECS tag key used to mark a task definition as the default image

const TaskDefinitionIsDefaultTagKey = "IsDefault"

TaskDefinitionIsDefaultTagValue is the tag value used to mark a task definition as the default image

const TaskDefinitionIsDefaultTagValue = "true"

func BuildLogStreamName

func BuildLogStreamName(executionID string) string

BuildLogStreamName constructs a CloudWatch Logs stream name for an execution. Format: task/{container}/{execution_id} Example: task/runner/abc123

func ExtractExecutionIDFromLogStream

func ExtractExecutionIDFromLogStream(logStream string) string

ExtractExecutionIDFromLogStream extracts the execution ID from a CloudWatch Logs stream name. Expected format: task/{container}/{execution_id} Returns empty string if the format is not recognized.

func GetReleaseRegions

func GetReleaseRegions() []string

GetReleaseRegions returns a slice of supported AWS regions for releases. The regions are parsed from the comma-separated string injected at build time.

func SupportedRuntimePlatforms

func SupportedRuntimePlatforms() []string

SupportedRuntimePlatforms returns the list of supported ECS runtime platforms.

func ValidateRegion

func ValidateRegion(region string) error

ValidateRegion validates that the given region is in the supported regions list. Returns an error if the region is not supported, or if no regions are configured.

type EcsStatus

EcsStatus represents the AWS ECS Task LastStatus lifecycle values. These are string statuses returned by ECS DescribeTasks for Task.LastStatus.

type EcsStatus string
const (
    // EcsStatusProvisioning represents a task being provisioned
    EcsStatusProvisioning EcsStatus = "PROVISIONING"
    // EcsStatusPending represents a task pending activation
    EcsStatusPending EcsStatus = "PENDING"
    // EcsStatusActivating represents a task being activated
    EcsStatusActivating EcsStatus = "ACTIVATING"
    // EcsStatusRunning represents a task currently running
    EcsStatusRunning EcsStatus = "RUNNING"
    // EcsStatusDeactivating represents a task being deactivated
    EcsStatusDeactivating EcsStatus = "DEACTIVATING"
    // EcsStatusStopping represents a task being stopped
    EcsStatusStopping EcsStatus = "STOPPING"
    // EcsStatusDeprovisioning represents a task being deprovisioned
    EcsStatusDeprovisioning EcsStatus = "DEPROVISIONING"
    // EcsStatusStopped represents a task that has stopped
    EcsStatusStopped EcsStatus = "STOPPED"
)

database

import "runvoy/internal/providers/aws/database"

Package database provides AWS-specific database implementations. This file contains a SecretsRepository that coordinates DynamoDB and Parameter Store.

Index

type MetadataRepository

MetadataRepository defines the interface for secret metadata operations.

type MetadataRepository interface {
    CreateSecret(ctx context.Context, secret *api.Secret) error
    GetSecret(ctx context.Context, name string) (*api.Secret, error)
    ListSecrets(ctx context.Context) ([]*api.Secret, error)
    UpdateSecretMetadata(ctx context.Context, name, keyName, description, updatedBy string) error
    DeleteSecret(ctx context.Context, name string) error
    SecretExists(ctx context.Context, name string) (bool, error)
    GetSecretsByRequestID(ctx context.Context, requestID string) ([]*api.Secret, error)
}

type Repositories

Repositories bundles all AWS-backed database repositories.

type Repositories struct {
    UserRepo         database.UserRepository
    ExecutionRepo    database.ExecutionRepository
    ConnectionRepo   database.ConnectionRepository
    TokenRepo        database.TokenRepository
    ImageTaskDefRepo *dynamoRepo.ImageTaskDefRepository
    SecretsRepo      database.SecretsRepository
}

func CreateRepositories

func CreateRepositories(dynamoClient dynamoRepo.Client, ssmClient secrets.Client, cfg *config.Config, log *slog.Logger) *Repositories

CreateRepositories creates all AWS-backed database repositories from the provided clients and configuration.

type SecretsRepository

SecretsRepository implements database.SecretsRepository for AWS. It coordinates DynamoDB (metadata) and Parameter Store (values) to provide a unified interface.

type SecretsRepository struct {
    // contains filtered or unexported fields
}

func NewSecretsRepository

func NewSecretsRepository(metadataRepo MetadataRepository, valueStore secrets.ValueStore, logger *slog.Logger) *SecretsRepository

NewSecretsRepository creates a new AWS secrets repository.

func (*SecretsRepository) CreateSecret

func (sr *SecretsRepository) CreateSecret(ctx context.Context, secret *api.Secret) error

CreateSecret stores a new secret with both metadata and value.

func (*SecretsRepository) DeleteSecret

func (sr *SecretsRepository) DeleteSecret(ctx context.Context, name string) error

DeleteSecret removes both the metadata and value of a secret.

func (*SecretsRepository) GetSecret

func (sr *SecretsRepository) GetSecret(ctx context.Context, name string, includeValue bool) (*api.Secret, error)

GetSecret retrieves a secret with its metadata and optionally its value.

func (*SecretsRepository) GetSecretsByRequestID

func (sr *SecretsRepository) GetSecretsByRequestID(ctx context.Context, requestID string) ([]*api.Secret, error)

GetSecretsByRequestID retrieves all secrets created or modified by a specific request ID.

func (*SecretsRepository) ListSecrets

func (sr *SecretsRepository) ListSecrets(ctx context.Context, includeValue bool) ([]*api.Secret, error)

ListSecrets retrieves all secrets with optionally their values.

func (*SecretsRepository) UpdateSecret

func (sr *SecretsRepository) UpdateSecret(ctx context.Context, secret *api.Secret) error

UpdateSecret updates a secret's metadata and/or value.

ecsdefs

import "runvoy/internal/providers/aws/ecsdefs"

Package ecsdefs contains shared ECS task definition utilities for AWS providers. It is intentionally decoupled from the orchestrator package so it can be reused by both the orchestrator and the health manager without creating import cycles.

Index

func BuildTaskDefinitionInputForConfig

func BuildTaskDefinitionInputForConfig(ctx context.Context, family, image, taskExecRoleARN, taskRoleARN, logGroup, region string, cpu, memory, runtimePlatform string) *ecs.RegisterTaskDefinitionInput

BuildTaskDefinitionInputForConfig creates the RegisterTaskDefinitionInput for a new task definition.

func BuildTaskDefinitionTags

func BuildTaskDefinitionTags(image string, isDefault *bool) []ecsTypes.Tag

BuildTaskDefinitionTags creates the tags to be applied to a task definition.

func RecreateTaskDefinition

func RecreateTaskDefinition(ctx context.Context, ecsClient awsClient.ECSClient, cfg *TaskDefinitionConfig, family string, image string, taskRoleARN string, taskExecRoleARN string, cpu, memory int, runtimePlatform string, isDefault bool, reqLogger *slog.Logger) (string, error)

RecreateTaskDefinition recreates a task definition from stored metadata. This function is used by the health manager to restore missing task definitions.

func UpdateTaskDefinitionTags

func UpdateTaskDefinitionTags(ctx context.Context, ecsClient awsClient.ECSClient, taskDefARN string, image string, isDefault bool, reqLogger *slog.Logger) error

UpdateTaskDefinitionTags updates tags on an existing task definition to match expected values.

type TaskDefinitionConfig

TaskDefinitionConfig contains configuration needed to build task definitions.

type TaskDefinitionConfig struct {
    LogGroup string
    Region   string
}

health

import "runvoy/internal/providers/aws/health"

Package health provides AWS-specific health management implementation for runvoy. It reconciles resources between DynamoDB metadata and actual AWS services.

Index

type Config

Config holds AWS-specific configuration for the health manager.

type Config struct {
    Region                 string
    AccountID              string
    DefaultTaskRoleARN     string
    DefaultTaskExecRoleARN string
    LogGroup               string
    SecretsPrefix          string
}

type ImageTaskDefRepository

ImageTaskDefRepository defines the interface for image-taskdef mapping operations.

type ImageTaskDefRepository interface {
    ListImages(ctx context.Context) ([]api.ImageInfo, error)
}

type Manager

Manager implements the health.Manager interface for AWS.

type Manager struct {
    // contains filtered or unexported fields
}

func Initialize

func Initialize(ecsClient awsClient.ECSClient, ssmClient secrets.Client, iamClient awsClient.IAMClient, imageRepo ImageTaskDefRepository, secretsRepo database.SecretsRepository, userRepo database.UserRepository, executionRepo database.ExecutionRepository, enforcer *authorization.Enforcer, cfg *Config, log *slog.Logger) *Manager

Initialize creates a new AWS health manager.

func (*Manager) Reconcile

func (m *Manager) Reconcile(ctx context.Context) (*api.HealthReport, error)

Reconcile performs health checks and reconciliation for ECS task definitions, SSM parameters, and IAM roles.

func (*Manager) SetCasbinDependencies

func (m *Manager) SetCasbinDependencies(userRepo database.UserRepository, executionRepo database.ExecutionRepository, enforcer *authorization.Enforcer)

SetCasbinDependencies sets the Casbin-related dependencies for the health manager. This allows the enforcer to be set after initialization when it becomes available.

identity

import "runvoy/internal/providers/aws/identity"

Package identity provides helpers for retrieving AWS identity information.

Index

func GetAccountID

func GetAccountID(ctx context.Context, awsCfg *awsStd.Config, log *slog.Logger) (string, error)

GetAccountID retrieves the AWS account ID using STS GetCallerIdentity.

lambdaapi

import "runvoy/internal/providers/aws/lambdaapi"

Package lambdaapi provides Lambda handler creation for AWS Lambda, integrating cloud-provider agnostic components with AWS-specific entry points.

Package lambdaapi provides Lambda handler creation for AWS Lambda Function URLs, integrating the application service with the HTTP router through algnhsa adapter.

Index

func NewEventProcessorHandler

func NewEventProcessorHandler(proc processor.Processor) lambda.Handler

NewEventProcessorHandler creates a new Lambda handler for event processing. It wraps the generic event processor and converts responses to AWS Lambda types, allowing the core processor to remain cloud-provider agnostic while still supporting AWS Lambda's specific response formats.

func NewHandler

func NewHandler(svc *orchestrator.Service, requestTimeout time.Duration, allowedOrigins []string) lambda.Handler

NewHandler creates a new Lambda handler with the given service. The request timeout is passed to the router to configure the timeout middleware. It uses algnhsa to adapt the chi router to work with Lambda Function URLs.

orchestrator

import "runvoy/internal/providers/aws/orchestrator"

Package orchestrator provides AWS-specific implementations for runvoy orchestrator. This file contains the ImageManager implementation for image registration and management.

Package orchestrator provides AWS-specific implementations for runvoy orchestrator. This file contains Docker image reference parsing utilities.

Package orchestrator provides AWS-specific implementations for runvoy orchestrator. This file contains image management using DynamoDB for image-taskdef mappings.

Package orchestrator provides AWS-specific implementations for runvoy orchestrator. This file contains the LogManager implementation for execution log retrieval.

Package orchestrator provides AWS-specific implementations for runvoy orchestrator. This file contains the ObservabilityManager implementation for backend log retrieval.

Package orchestrator provides AWS-specific implementations for runvoy orchestrator. It handles ECS task execution and AWS service integration.

Package orchestrator provides AWS-specific implementations for runvoy orchestrator. This file contains shared tagging utilities for AWS resources.

Package orchestrator provides AWS-specific implementations for runvoy orchestrator.

Index

func BuildTaskDefinitionInput

func BuildTaskDefinitionInput(ctx context.Context, family, image, taskExecRoleARN, taskRoleARN, region string, cpu, memory int, runtimePlatform string, cfg *Config) *ecs.RegisterTaskDefinitionInput

BuildTaskDefinitionInput creates the ECS RegisterTaskDefinitionInput for a new task definition.

func DeregisterTaskDefinitionsForImage

func DeregisterTaskDefinitionsForImage(ctx context.Context, ecsClient awsClient.ECSClient, image string, log *slog.Logger) error

DeregisterTaskDefinitionsForImage deregisters all task definition revisions for a given image. If the removed image was the default and only one image remains, that image becomes the new default.

func ExtractImageFromTaskDefFamily

func ExtractImageFromTaskDefFamily(familyName string) string

ExtractImageFromTaskDefFamily extracts the Docker image name from a task definition family name. Returns empty string if the family name doesn't match the expected format. NOTE: This is approximate - images should be read from container definitions or tags, not family names.

func GetDefaultImage

func GetDefaultImage(ctx context.Context, ecsClient awsClient.ECSClient, log *slog.Logger) (string, error)

GetDefaultImage returns the Docker image marked as default (via IsDefault tag). Returns empty string if no default image is found.

func GetStandardECSTags

func GetStandardECSTags() []ecsTypes.Tag

GetStandardECSTags returns the standard tags in ECS tag format.

func GetTaskDefinitionForImage

func GetTaskDefinitionForImage(ctx context.Context, ecsClient awsClient.ECSClient, image string, log *slog.Logger) (string, error)

GetTaskDefinitionForImage looks up an existing task definition for the given Docker image. Returns an error if the task definition doesn't exist (does not auto-register).

func SanitizeImageNameForTaskDef

func SanitizeImageNameForTaskDef(image string) string

SanitizeImageNameForTaskDef converts a Docker image name to a valid ECS task definition family name. ECS task definition family names must match: [a-zA-Z0-9_-]+ Examples:

  • "hashicorp/terraform:1.6" -> "hashicorp-terraform-1-6"
  • "myregistry.com/my-image:latest" -> "myregistry-com-my-image-latest"

func TaskDefinitionFamilyName

func TaskDefinitionFamilyName(image string) string

TaskDefinitionFamilyName returns the ECS task definition family name for a given image. Format: {TaskDefinitionFamilyPrefix}-{sanitized-image-name}

type Config

Config holds AWS-specific execution configuration.

type Config struct {
    ECSCluster             string
    TaskDefinition         string
    Subnet1                string
    Subnet2                string
    SecurityGroup          string
    LogGroup               string
    DefaultTaskRoleARN     string
    DefaultTaskExecRoleARN string
    Region                 string
    AccountID              string
    SDKConfig              *awsStd.Config
}

type Dependencies

Dependencies bundles the AWS-backed implementations required by the app service.

type Dependencies struct {
    UserRepo             database.UserRepository
    ExecutionRepo        database.ExecutionRepository
    ConnectionRepo       database.ConnectionRepository
    TokenRepo            database.TokenRepository
    ImageRepo            database.ImageRepository
    TaskManager          contract.TaskManager
    ImageRegistry        contract.ImageRegistry
    LogManager           contract.LogManager
    ObservabilityManager contract.ObservabilityManager
    WebSocketManager     contract.WebSocketManager
    SecretsRepo          database.SecretsRepository
    HealthManager        contract.HealthManager
}

func Initialize

func Initialize(ctx context.Context, cfg *config.Config, log *slog.Logger, enforcer *authorization.Enforcer) (*Dependencies, error)

Initialize prepares AWS service dependencies for the app package. Wraps the AWS SDK clients in adapters for improved testability.

type ImageReference

ImageReference represents a parsed Docker image reference.

type ImageReference struct {
    // Full is the complete image reference as provided
    Full string
    // Registry is the registry hostname (empty string = Docker Hub)
    // Examples: "", "123456.dkr.ecr.us-east-1.amazonaws.com", "gcr.io"
    Registry string
    // Name is the image name (may include org/repo structure)
    // Examples: "alpine", "hashicorp/terraform", "myapp"
    Name string
    // Tag is the image tag or digest
    // Examples: "latest", "1.6", "sha256:abc123..."
    Tag string
}

func ParseImageReference

func ParseImageReference(image string) ImageReference

ParseImageReference parses a Docker image reference into its components. Supports formats:

  • alpine:latest → registry="", name="alpine", tag="latest"
  • ubuntu → registry="", name="ubuntu", tag="latest"
  • myorg/myapp:v1.0 → registry="", name="myorg/myapp", tag="v1.0"
  • 123456.dkr.ecr.us-east-1.amazonaws.com/myapp:v1.0 → registry="123456.dkr.ecr.us-east-1.amazonaws.com", name="myapp", tag="v1.0"
  • gcr.io/project/image:tag → registry="gcr.io", name="project/image", tag="tag"

func (ImageReference) IsDockerHub

func (r ImageReference) IsDockerHub() bool

IsDockerHub returns true if the image is from Docker Hub.

func (ImageReference) IsECR

func (r ImageReference) IsECR() bool

IsECR returns true if the image is from AWS ECR.

func (ImageReference) NameWithTag

func (r ImageReference) NameWithTag() string

NameWithTag returns the image name with tag but without registry. Examples: "alpine:latest", "hashicorp/terraform:1.6"

func (ImageReference) NormalizeRegistry

func (r ImageReference) NormalizeRegistry() string

NormalizeRegistry returns a normalized registry identifier. Returns "docker.io" for Docker Hub (empty registry), otherwise returns the registry as-is.

func (ImageReference) ShortName

func (r ImageReference) ShortName() string

ShortName returns the image name without registry or tag. Examples: "alpine", "hashicorp/terraform", "myapp"

type ImageRegistryImpl

ImageRegistryImpl implements the ImageRegistry interface for AWS ECS and DynamoDB. It handles Docker image registration, listing, retrieval, and removal.

type ImageRegistryImpl struct {
    // contains filtered or unexported fields
}

func NewImageRegistry

func NewImageRegistry(ecsClient awsClient.ECSClient, iamClient awsClient.IAMClient, imageRepo ImageTaskDefRepository, cfg *Config, log *slog.Logger) *ImageRegistryImpl

NewImageRegistry creates a new AWS image manager.

func (*ImageRegistryImpl) GetImage

func (m *ImageRegistryImpl) GetImage(ctx context.Context, image string) (*api.ImageInfo, error)

GetImage retrieves a single Docker image by ID or name. Accepts either an ImageID (e.g., "alpine:latest-a1b2c3d4") or an image name (e.g., "alpine:latest"). If ImageID is provided, queries directly by ID. Otherwise, uses GetAnyImageTaskDef to find any configuration. If image is empty, returns the default image if one is configured.

func (*ImageRegistryImpl) ListImages

func (m *ImageRegistryImpl) ListImages(ctx context.Context) ([]api.ImageInfo, error)

ListImages lists all registered Docker images from DynamoDB.

func (*ImageRegistryImpl) RegisterImage

func (m *ImageRegistryImpl) RegisterImage(ctx context.Context, image string, isDefault *bool, taskRoleName *string, taskExecutionRoleName *string, cpu *int, memory *int, runtimePlatform *string, createdBy string) error

RegisterImage registers a Docker image with optional custom IAM roles, CPU, Memory, and RuntimePlatform. Creates a new task definition with a unique family name and stores the mapping in DynamoDB.

func (*ImageRegistryImpl) RemoveImage

func (m *ImageRegistryImpl) RemoveImage(ctx context.Context, image string) error

RemoveImage removes a Docker image and all its task definition variants from DynamoDB. It also deregisters all associated task definitions from ECS. If deregistration fails for any task definition, it continues to clean up the remaining ones and still removes the mappings from DynamoDB.

NOTE: To avoid accidental deletion of multiple image configurations, this function requires the full ImageID (e.g., "alpine:latest-a1b2c3d4") instead of just the image name/tag. Use ListImages to find the specific ImageID you want to remove.

type ImageTaskDefRepository

ImageTaskDefRepository defines the interface for image-taskdef mapping operations.

type ImageTaskDefRepository interface {
    PutImageTaskDef(
        ctx context.Context,
        imageID string,
        image string,
        imageRegistry string,
        imageName string,
        imageTag string,
        taskRoleName, taskExecutionRoleName *string,
        cpu, memory int,
        runtimePlatform string,
        taskDefFamily string,
        isDefault bool,
        registeredBy string,
    ) error
    GetImageTaskDef(
        ctx context.Context,
        image string,
        taskRoleName, taskExecutionRoleName *string,
        cpu, memory *int,
        runtimePlatform *string,
    ) (*api.ImageInfo, error)
    GetImageTaskDefByID(ctx context.Context, imageID string) (*api.ImageInfo, error)
    GetAnyImageTaskDef(ctx context.Context, image string) (*api.ImageInfo, error)
    ListImages(ctx context.Context) ([]api.ImageInfo, error)
    GetDefaultImage(ctx context.Context) (*api.ImageInfo, error)
    UnmarkAllDefaults(ctx context.Context) error
    DeleteImage(ctx context.Context, image string) error
    SetImageAsOnlyDefault(ctx context.Context, image string, taskRoleName, taskExecutionRoleName *string) error
    GetImagesByRequestID(ctx context.Context, requestID string) ([]api.ImageInfo, error)
}

type LambdaContextExtractor

LambdaContextExtractor extracts request IDs from AWS Lambda contexts.

type LambdaContextExtractor struct{}

func NewLambdaContextExtractor

func NewLambdaContextExtractor() *LambdaContextExtractor

NewLambdaContextExtractor creates a new AWS Lambda context extractor.

func (*LambdaContextExtractor) ExtractRequestID

func (e *LambdaContextExtractor) ExtractRequestID(ctx context.Context) (string, bool)

ExtractRequestID extracts the AWS request ID from a Lambda context.

type LogManagerImpl

LogManagerImpl implements the LogManager interface for AWS CloudWatch Logs. It handles retrieving execution logs from CloudWatch.

type LogManagerImpl struct {
    // contains filtered or unexported fields
}

func NewLogManager

func NewLogManager(cwlClient awsClient.CloudWatchLogsClient, cfg *Config, log *slog.Logger) *LogManagerImpl

NewLogManager creates a new AWS log manager.

func (*LogManagerImpl) FetchLogsByExecutionID

func (l *LogManagerImpl) FetchLogsByExecutionID(ctx context.Context, executionID string) ([]api.LogEvent, error)

FetchLogsByExecutionID returns CloudWatch log events for the given execution ID.

type ObservabilityManagerImpl

ObservabilityManagerImpl implements the ObservabilityManager interface for AWS CloudWatch Logs Insights. It handles retrieving backend infrastructure logs for debugging and tracing.

type ObservabilityManagerImpl struct {
    // contains filtered or unexported fields
}

func NewObservabilityManager

func NewObservabilityManager(cwlClient awsClient.CloudWatchLogsClient, log *slog.Logger) *ObservabilityManagerImpl

NewObservabilityManager creates a new AWS observability manager.

func (*ObservabilityManagerImpl) FetchBackendLogs

func (o *ObservabilityManagerImpl) FetchBackendLogs(ctx context.Context, requestID string) ([]api.LogEvent, error)

FetchBackendLogs retrieves backend infrastructure logs using CloudWatch Logs Insights Queries logs from Lambda execution for debugging and tracing

type TaskManagerImpl

TaskManagerImpl implements the TaskManager interface for AWS ECS Fargate. It handles task lifecycle management including starting and terminating tasks.

type TaskManagerImpl struct {
    // contains filtered or unexported fields
}

func NewTaskManager

func NewTaskManager(ecsClient awsClient.ECSClient, imageRepo ImageTaskDefRepository, cfg *Config, log *slog.Logger) *TaskManagerImpl

NewTaskManager creates a new AWS ECS task manager.

func (*TaskManagerImpl) KillTask

func (t *TaskManagerImpl) KillTask(ctx context.Context, executionID string) error

KillTask terminates an ECS task identified by executionID. It checks the task status before termination and only stops tasks that are RUNNING or ACTIVATING. Returns an error if the task is already terminated or not found.

func (*TaskManagerImpl) StartTask

func (t *TaskManagerImpl) StartTask(ctx context.Context, userEmail string, req *api.ExecutionRequest) (string, *time.Time, error)

StartTask triggers an ECS Fargate task and returns identifiers.

aws

import "runvoy/internal/providers/aws/processor"

Package aws provides AWS-specific event processing implementations.

Index

func ParseTime

func ParseTime(timeStr string) (time.Time, error)

ParseTime parses an RFC3339 timestamp string

type ContainerDetail

ContainerDetail represents a container within an ECS task

type ContainerDetail struct {
    ContainerArn string `json:"containerArn"`
    Name         string `json:"name"`
    ExitCode     *int   `json:"exitCode,omitempty"`
    Reason       string `json:"reason,omitempty"`
}

type ECSTaskStateChangeEvent

ECSTaskStateChangeEvent represents the detail structure of an ECS Task State Change event

type ECSTaskStateChangeEvent struct {
    ClusterArn    string            `json:"clusterArn"`
    TaskArn       string            `json:"taskArn"`
    LastStatus    string            `json:"lastStatus"`
    DesiredStatus string            `json:"desiredStatus"`
    Containers    []ContainerDetail `json:"containers"`
    StartedAt     string            `json:"startedAt"`
    StoppedAt     string            `json:"stoppedAt"`
    StoppedReason string            `json:"stoppedReason"`
    StopCode      string            `json:"stopCode"`
    CPU           string            `json:"cpu"`
    Memory        string            `json:"memory"`
}

type Processor

Processor implements the events.Processor interface for AWS. It handles CloudWatch events, CloudWatch Logs, API Gateway WebSocket events, and scheduled events.

type Processor struct {
    // contains filtered or unexported fields
}

func Initialize

func Initialize(ctx context.Context, cfg *config.Config, enforcer *authorization.Enforcer, log *slog.Logger) (*Processor, error)

Initialize constructs an AWS-backed event processor with all required dependencies. Wraps the AWS SDK clients in adapters for improved testability.

func NewProcessor

func NewProcessor(executionRepo database.ExecutionRepository, webSocketManager contract.WebSocketManager, healthManager contract.HealthManager, log *slog.Logger) *Processor

NewProcessor creates a new AWS event processor.

func (*Processor) Handle

func (p *Processor) Handle(ctx context.Context, rawEvent *json.RawMessage) (*json.RawMessage, error)

Handle processes a raw AWS event by delegating to the appropriate handler. It supports CloudWatch events, CloudWatch Logs, and WebSocket events.

func (*Processor) HandleEventJSON

func (p *Processor) HandleEventJSON(ctx context.Context, eventJSON *json.RawMessage) error

HandleEventJSON is a helper for testing that accepts raw JSON and returns an error. It's used for test cases that expect error returns.

secrets

import "runvoy/internal/providers/aws/secrets"

Package secrets provides secret management functionality for the Runvoy orchestrator. This file contains the AWS Systems Manager Parameter Store implementation.

Index

type Client

Client defines the interface for SSM operations used by the ParameterStoreManager. This interface makes the code easier to test by allowing mock implementations.

type Client interface {
    PutParameter(
        ctx context.Context,
        params *ssm.PutParameterInput,
        optFns ...func(*ssm.Options),
    ) (*ssm.PutParameterOutput, error)
    AddTagsToResource(
        ctx context.Context,
        params *ssm.AddTagsToResourceInput,
        optFns ...func(*ssm.Options),
    ) (*ssm.AddTagsToResourceOutput, error)
    GetParameter(
        ctx context.Context,
        params *ssm.GetParameterInput,
        optFns ...func(*ssm.Options),
    ) (*ssm.GetParameterOutput, error)
    DeleteParameter(
        ctx context.Context,
        params *ssm.DeleteParameterInput,
        optFns ...func(*ssm.Options),
    ) (*ssm.DeleteParameterOutput, error)
    ListTagsForResource(
        ctx context.Context,
        params *ssm.ListTagsForResourceInput,
        optFns ...func(*ssm.Options),
    ) (*ssm.ListTagsForResourceOutput, error)
    DescribeParameters(
        ctx context.Context,
        params *ssm.DescribeParametersInput,
        optFns ...func(*ssm.Options),
    ) (*ssm.DescribeParametersOutput, error)
}

type ClientAdapter

ClientAdapter wraps the AWS SDK SSM client to implement Client interface. This allows us to use the real AWS client in production while maintaining testability.

type ClientAdapter struct {
    // contains filtered or unexported fields
}

func NewClientAdapter

func NewClientAdapter(client *ssm.Client) *ClientAdapter

NewClientAdapter creates a new adapter wrapping the AWS SDK SSM client.

func (*ClientAdapter) AddTagsToResource

func (a *ClientAdapter) AddTagsToResource(ctx context.Context, params *ssm.AddTagsToResourceInput, optFns ...func(*ssm.Options)) (*ssm.AddTagsToResourceOutput, error)

AddTagsToResource wraps the AWS SDK AddTagsToResource operation.

func (*ClientAdapter) DeleteParameter

func (a *ClientAdapter) DeleteParameter(ctx context.Context, params *ssm.DeleteParameterInput, optFns ...func(*ssm.Options)) (*ssm.DeleteParameterOutput, error)

DeleteParameter wraps the AWS SDK DeleteParameter operation.

func (*ClientAdapter) DescribeParameters

func (a *ClientAdapter) DescribeParameters(ctx context.Context, params *ssm.DescribeParametersInput, optFns ...func(*ssm.Options)) (*ssm.DescribeParametersOutput, error)

DescribeParameters wraps the AWS SDK DescribeParameters operation.

func (*ClientAdapter) GetParameter

func (a *ClientAdapter) GetParameter(ctx context.Context, params *ssm.GetParameterInput, optFns ...func(*ssm.Options)) (*ssm.GetParameterOutput, error)

GetParameter wraps the AWS SDK GetParameter operation.

func (*ClientAdapter) ListTagsForResource

func (a *ClientAdapter) ListTagsForResource(ctx context.Context, params *ssm.ListTagsForResourceInput, optFns ...func(*ssm.Options)) (*ssm.ListTagsForResourceOutput, error)

ListTagsForResource wraps the AWS SDK ListTagsForResource operation.

func (*ClientAdapter) PutParameter

func (a *ClientAdapter) PutParameter(ctx context.Context, params *ssm.PutParameterInput, optFns ...func(*ssm.Options)) (*ssm.PutParameterOutput, error)

PutParameter wraps the AWS SDK PutParameter operation.

type ParameterStoreManager

ParameterStoreManager implements secret value storage using AWS Systems Manager Parameter Store. Secrets are stored as SecureString parameters and encrypted with KMS.

type ParameterStoreManager struct {
    // contains filtered or unexported fields
}

func NewParameterStoreManager

func NewParameterStoreManager(client Client, secretPrefix, kmsKeyARN string, log *slog.Logger) *ParameterStoreManager

NewParameterStoreManager creates a new Parameter Store-based secrets manager. secretPrefix should include a leading slash, e.g., "/runvoy/secrets"

func (*ParameterStoreManager) DeleteSecret

func (m *ParameterStoreManager) DeleteSecret(ctx context.Context, name string) error

DeleteSecret removes a secret from AWS Systems Manager Parameter Store.

func (*ParameterStoreManager) RetrieveSecret

func (m *ParameterStoreManager) RetrieveSecret(ctx context.Context, name string) (string, error)

RetrieveSecret retrieves a secret value from AWS Systems Manager Parameter Store. The value is automatically decrypted using the KMS key.

func (*ParameterStoreManager) StoreSecret

func (m *ParameterStoreManager) StoreSecret(ctx context.Context, name, value string) error

StoreSecret saves a secret value to AWS Systems Manager Parameter Store as a SecureString. The value is encrypted with the KMS key specified during initialization.

type StandardTag

StandardTag represents a standard AWS resource tag as key-value pairs.

type StandardTag struct {
    Key   string
    Value string
}

func GetStandardTags

func GetStandardTags() []StandardTag

GetStandardTags returns the standard tags applied to all AWS resources managed by runvoy at runtime. These tags are used for resource identification and management tracking.

type ValueStore

ValueStore is the interface for storing and retrieving encrypted secret values. Implementations handle the actual secret payloads (e.g., in AWS Parameter Store).

type ValueStore interface {
    // StoreSecret saves a secret value and returns any error.
    StoreSecret(ctx context.Context, name, value string) error

    // RetrieveSecret gets a secret value by name.
    RetrieveSecret(ctx context.Context, name string) (string, error)

    // DeleteSecret removes a secret value by name.
    DeleteSecret(ctx context.Context, name string) error
}

websocket

import "runvoy/internal/providers/aws/websocket"

Package websocket provides AWS-specific WebSocket management implementation for runvoy. It handles connection lifecycle events via API Gateway and manages WebSocket connections in DynamoDB.

Index

type Client

Client defines the interface for API Gateway Management API operations used by the Manager. This interface makes the code easier to test by allowing mock implementations.

type Client interface {
    PostToConnection(
        ctx context.Context,
        params *apigatewaymanagementapi.PostToConnectionInput,
        optFns ...func(*apigatewaymanagementapi.Options),
    ) (*apigatewaymanagementapi.PostToConnectionOutput, error)
}

type ClientAdapter

ClientAdapter wraps the AWS SDK API Gateway Management API client to implement Client interface. This allows us to use the real AWS client in production while maintaining testability.

type ClientAdapter struct {
    // contains filtered or unexported fields
}

func NewClientAdapter

func NewClientAdapter(client *apigatewaymanagementapi.Client) *ClientAdapter

NewClientAdapter creates a new adapter wrapping the AWS SDK API Gateway Management API client.

func (*ClientAdapter) PostToConnection

func (a *ClientAdapter) PostToConnection(ctx context.Context, params *apigatewaymanagementapi.PostToConnectionInput, optFns ...func(*apigatewaymanagementapi.Options)) (*apigatewaymanagementapi.PostToConnectionOutput, error)

PostToConnection wraps the AWS SDK PostToConnection operation.

type Manager

Manager implements the websocket.Manager interface for AWS. It uses API Gateway Management API to communicate with WebSocket clients and DynamoDB to store connection metadata.

type Manager struct {
    // contains filtered or unexported fields
}

func Initialize

func Initialize(cfg *config.Config, connRepo database.ConnectionRepository, tokenRepo database.TokenRepository, log *slog.Logger) *Manager

Initialize creates a new AWS WebSocket manager.

func (*Manager) GenerateWebSocketURL

func (m *Manager) GenerateWebSocketURL(ctx context.Context, executionID string, userEmail *string, clientIPAtCreationTime *string) string

GenerateWebSocketURL creates a WebSocket token and returns the connection URL. It stores the token for validation when the client connects.

func (*Manager) HandleRequest

func (m *Manager) HandleRequest(ctx context.Context, rawEvent *json.RawMessage, reqLogger *slog.Logger) (bool, error)

HandleRequest adapts WebSocket events so the generic event processor can route them. It attempts to unmarshal the raw Lambda event as an API Gateway WebSocket request and, when successful, dispatches based on the route key.

func (*Manager) NotifyExecutionCompletion

func (m *Manager) NotifyExecutionCompletion(ctx context.Context, executionID *string) error

NotifyExecutionCompletion sends disconnect notifications to all connected clients for an execution and deletes the connections from DynamoDB.

func (*Manager) SendLogsToExecution

func (m *Manager) SendLogsToExecution(ctx context.Context, executionID *string, logEvents []api.LogEvent) error

SendLogsToExecution sends log events to all connected clients for an execution. Each log event is sent individually to all connected clients concurrently.

orchestrator

import "runvoy/cmd/backend/providers/aws/orchestrator"

Package main implements the AWS Lambda orchestrator for runvoy. It handles API requests and orchestrates ECS task executions.

Index

processor

import "runvoy/cmd/backend/providers/aws/processor"

Package main implements the AWS Lambda event processor for runvoy. It processes various AWS Lambda events including CloudWatch events related to ECS task completions and API Gateway WebSocket events.

Index

dynamodb

import "runvoy/internal/providers/aws/database/dynamodb"

Package dynamodb implements DynamoDB-based storage for runvoy. It provides persistence for execution records using AWS DynamoDB.

Package dynamodb implements DynamoDB-based storage for runvoy. It provides persistence for image-taskdef mappings using AWS DynamoDB.

Index

func GenerateImageID

func GenerateImageID(imageName, imageTag string, cpu, memory int, runtimePlatform string, taskRoleName, taskExecutionRoleName *string) string

GenerateImageID generates a unique, human-readable ID for an image configuration. Format: {imageName}:{tag}-{first-8-chars-of-hash} Example: alpine:latest-a1b2c3d4 or golang:1.24.5-bookworm-19884ca2

func NewConnectionRepository

func NewConnectionRepository(client Client, tableName string, log *slog.Logger) database.ConnectionRepository

NewConnectionRepository creates a new DynamoDB-backed connection repository.

func NewTokenRepository

func NewTokenRepository(client Client, tableName string, log *slog.Logger) database.TokenRepository

NewTokenRepository creates a new DynamoDB-backed token repository.

type Client

Client defines the interface for DynamoDB operations used by repositories. This interface makes repositories easier to test by allowing mock implementations.

type Client interface {
    PutItem(
        ctx context.Context,
        params *dynamodb.PutItemInput,
        optFns ...func(*dynamodb.Options),
    ) (*dynamodb.PutItemOutput, error)
    GetItem(
        ctx context.Context,
        params *dynamodb.GetItemInput,
        optFns ...func(*dynamodb.Options),
    ) (*dynamodb.GetItemOutput, error)
    Query(
        ctx context.Context,
        params *dynamodb.QueryInput,
        optFns ...func(*dynamodb.Options),
    ) (*dynamodb.QueryOutput, error)
    UpdateItem(
        ctx context.Context,
        params *dynamodb.UpdateItemInput,
        optFns ...func(*dynamodb.Options),
    ) (*dynamodb.UpdateItemOutput, error)
    DeleteItem(
        ctx context.Context,
        params *dynamodb.DeleteItemInput,
        optFns ...func(*dynamodb.Options),
    ) (*dynamodb.DeleteItemOutput, error)
    BatchWriteItem(
        ctx context.Context,
        params *dynamodb.BatchWriteItemInput,
        optFns ...func(*dynamodb.Options),
    ) (*dynamodb.BatchWriteItemOutput, error)
    Scan(
        ctx context.Context,
        params *dynamodb.ScanInput,
        optFns ...func(*dynamodb.Options),
    ) (*dynamodb.ScanOutput, error)
}

type ClientAdapter

ClientAdapter wraps the AWS SDK DynamoDB client to implement Client interface. This allows us to use the real AWS client in production while maintaining testability.

type ClientAdapter struct {
    // contains filtered or unexported fields
}

func NewClientAdapter

func NewClientAdapter(client *dynamodb.Client) *ClientAdapter

NewClientAdapter creates a new adapter wrapping the AWS SDK DynamoDB client.

func (*ClientAdapter) BatchWriteItem

func (a *ClientAdapter) BatchWriteItem(ctx context.Context, params *dynamodb.BatchWriteItemInput, optFns ...func(*dynamodb.Options)) (*dynamodb.BatchWriteItemOutput, error)

BatchWriteItem wraps the AWS SDK BatchWriteItem operation.

func (*ClientAdapter) DeleteItem

func (a *ClientAdapter) DeleteItem(ctx context.Context, params *dynamodb.DeleteItemInput, optFns ...func(*dynamodb.Options)) (*dynamodb.DeleteItemOutput, error)

DeleteItem wraps the AWS SDK DeleteItem operation.

func (*ClientAdapter) GetItem

func (a *ClientAdapter) GetItem(ctx context.Context, params *dynamodb.GetItemInput, optFns ...func(*dynamodb.Options)) (*dynamodb.GetItemOutput, error)

GetItem wraps the AWS SDK GetItem operation.

func (*ClientAdapter) PutItem

func (a *ClientAdapter) PutItem(ctx context.Context, params *dynamodb.PutItemInput, optFns ...func(*dynamodb.Options)) (*dynamodb.PutItemOutput, error)

PutItem wraps the AWS SDK PutItem operation.

func (*ClientAdapter) Query

func (a *ClientAdapter) Query(ctx context.Context, params *dynamodb.QueryInput, optFns ...func(*dynamodb.Options)) (*dynamodb.QueryOutput, error)

Query wraps the AWS SDK Query operation.

func (*ClientAdapter) Scan

func (a *ClientAdapter) Scan(ctx context.Context, params *dynamodb.ScanInput, optFns ...func(*dynamodb.Options)) (*dynamodb.ScanOutput, error)

Scan wraps the AWS SDK Scan operation.

func (*ClientAdapter) UpdateItem

func (a *ClientAdapter) UpdateItem(ctx context.Context, params *dynamodb.UpdateItemInput, optFns ...func(*dynamodb.Options)) (*dynamodb.UpdateItemOutput, error)

UpdateItem wraps the AWS SDK UpdateItem operation.

type ConnectionRepository

ConnectionRepository implements the database.ConnectionRepository interface using DynamoDB.

type ConnectionRepository struct {
    // contains filtered or unexported fields
}

func (*ConnectionRepository) CreateConnection

func (r *ConnectionRepository) CreateConnection(ctx context.Context, connection *api.WebSocketConnection) error

CreateConnection stores a new WebSocket connection record in DynamoDB.

func (*ConnectionRepository) DeleteConnections

func (r *ConnectionRepository) DeleteConnections(ctx context.Context, connectionIDs []string) (int, error)

DeleteConnections removes WebSocket connections from DynamoDB by connection IDs using batch delete.

func (*ConnectionRepository) GetConnectionsByExecutionID

func (r *ConnectionRepository) GetConnectionsByExecutionID(ctx context.Context, executionID string) ([]*api.WebSocketConnection, error)

GetConnectionsByExecutionID retrieves all active WebSocket connection records for a given execution ID using the execution_id-index GSI.

type ExecutionRepository

ExecutionRepository implements the database.ExecutionRepository interface using DynamoDB.

type ExecutionRepository struct {
    // contains filtered or unexported fields
}

func NewExecutionRepository

func NewExecutionRepository(client Client, tableName string, log *slog.Logger) *ExecutionRepository

NewExecutionRepository creates a new DynamoDB-backed execution repository.

func (*ExecutionRepository) CreateExecution

func (r *ExecutionRepository) CreateExecution(ctx context.Context, execution *api.Execution) error

CreateExecution stores a new execution record in DynamoDB.

func (*ExecutionRepository) GetExecution

func (r *ExecutionRepository) GetExecution(ctx context.Context, executionID string) (*api.Execution, error)

GetExecution retrieves an execution by its execution ID.

func (*ExecutionRepository) GetExecutionsByRequestID

func (r *ExecutionRepository) GetExecutionsByRequestID(ctx context.Context, requestID string) ([]*api.Execution, error)

GetExecutionsByRequestID retrieves all executions created or modified by a specific request ID. This uses Query operations on two GSIs (created_by_request_id-index and modified_by_request_id-index) instead of Scan for better performance.

func (*ExecutionRepository) ListExecutions

func (r *ExecutionRepository) ListExecutions(ctx context.Context, limit int, statuses []string) ([]*api.Execution, error)

ListExecutions queries the executions table using the all-started_at GSI to return execution records sorted by StartedAt descending (newest first). This uses Query instead of Scan for better performance and native sorting by DynamoDB. Status filtering and limiting are handled natively by DynamoDB using FilterExpression and Limit.

Parameters:

  • limit: maximum number of executions to return. Use 0 to return all executions.
  • statuses: optional slice of execution statuses to filter by. If empty, all executions are returned.

func (*ExecutionRepository) UpdateExecution

func (r *ExecutionRepository) UpdateExecution(ctx context.Context, execution *api.Execution) error

UpdateExecution updates an existing execution record.

type ImageTaskDefRepository

ImageTaskDefRepository implements image-taskdef mapping operations using DynamoDB.

type ImageTaskDefRepository struct {
    // contains filtered or unexported fields
}

func NewImageTaskDefRepository

func NewImageTaskDefRepository(client Client, tableName string, log *slog.Logger) *ImageTaskDefRepository

NewImageTaskDefRepository creates a new DynamoDB-backed image-taskdef repository.

func (*ImageTaskDefRepository) DeleteImage

func (r *ImageTaskDefRepository) DeleteImage(ctx context.Context, image string) error

DeleteImage removes all task definition mappings for a specific image. Supports exact matching on both the image field and image_id field (for ImageID format). Returns ErrNotFound if no matching images are found.

func (*ImageTaskDefRepository) GetAnyImageTaskDef

func (r *ImageTaskDefRepository) GetAnyImageTaskDef(ctx context.Context, image string) (*api.ImageInfo, error)

GetAnyImageTaskDef retrieves any task definition configuration for a given image. Supports flexible matching: tries exact match on full image first, then matches by name:tag components. Returns the first matching item, preferring the default configuration if available.

func (*ImageTaskDefRepository) GetDefaultImage

func (r *ImageTaskDefRepository) GetDefaultImage(ctx context.Context) (*api.ImageInfo, error)

GetDefaultImage retrieves the image marked as default.

func (*ImageTaskDefRepository) GetImageTaskDef

func (r *ImageTaskDefRepository) GetImageTaskDef(ctx context.Context, image string, taskRoleName *string, taskExecutionRoleName *string, cpu *int, memory *int, runtimePlatform *string) (*api.ImageInfo, error)

GetImageTaskDef retrieves a specific image-taskdef mapping by generating ImageID from the configuration.

func (*ImageTaskDefRepository) GetImageTaskDefByID

func (r *ImageTaskDefRepository) GetImageTaskDefByID(ctx context.Context, imageID string) (*api.ImageInfo, error)

GetImageTaskDefByID retrieves an image-taskdef mapping by ImageID.

func (*ImageTaskDefRepository) GetImagesByRequestID

func (r *ImageTaskDefRepository) GetImagesByRequestID(ctx context.Context, requestID string) ([]api.ImageInfo, error)

GetImagesByRequestID retrieves all images created or modified by a specific request ID.

func (*ImageTaskDefRepository) GetImagesCount

func (r *ImageTaskDefRepository) GetImagesCount(ctx context.Context) (int, error)

GetImagesCount returns the total number of unique image+role combinations.

func (*ImageTaskDefRepository) GetUniqueImages

func (r *ImageTaskDefRepository) GetUniqueImages(ctx context.Context) ([]string, error)

GetUniqueImages returns a list of unique image names (deduplicated across role combinations).

func (*ImageTaskDefRepository) ListImages

func (r *ImageTaskDefRepository) ListImages(ctx context.Context) ([]api.ImageInfo, error)

ListImages retrieves all registered images with their task definitions.

func (*ImageTaskDefRepository) PutImageTaskDef

func (r *ImageTaskDefRepository) PutImageTaskDef(ctx context.Context, imageID string, image string, imageRegistry string, imageName string, imageTag string, taskRoleName *string, taskExecutionRoleName *string, cpu int, memory int, runtimePlatform string, taskDefFamily string, isDefault bool, createdBy string) error

PutImageTaskDef stores or updates an image-taskdef mapping.

func (*ImageTaskDefRepository) SetImageAsOnlyDefault

func (r *ImageTaskDefRepository) SetImageAsOnlyDefault(ctx context.Context, image string, taskRoleName *string, taskExecutionRoleName *string) error

SetImageAsOnlyDefault marks a specific image configuration as the only default. It first unmarks all other defaults, then sets this one as default.

func (*ImageTaskDefRepository) UnmarkAllDefaults

func (r *ImageTaskDefRepository) UnmarkAllDefaults(ctx context.Context) error

UnmarkAllDefaults removes the default flag from all images.

type MockDynamoDBClient

MockDynamoDBClient is a simple in-memory mock implementation of Client for testing. It provides basic support for Put, Get, Query, Update, Delete, and BatchWrite operations.

type MockDynamoDBClient struct {

    // Tables maps table name -> partition key -> sort key -> item
    // For tables without sort key, use empty string as sort key
    Tables map[string]map[string]map[string]map[string]types.AttributeValue

    // Index stores items by index name for Query operations
    // Format: tableName -> indexName -> keyValue -> list of items
    Indexes map[string]map[string]map[string][]map[string]types.AttributeValue

    // Error injection for testing error scenarios
    PutItemError        error
    GetItemError        error
    QueryError          error
    UpdateItemError     error
    DeleteItemError     error
    BatchWriteItemError error
    ScanError           error

    // Call tracking for test assertions
    PutItemCalls        int
    GetItemCalls        int
    QueryCalls          int
    UpdateItemCalls     int
    DeleteItemCalls     int
    BatchWriteItemCalls int
    ScanCalls           int
    // contains filtered or unexported fields
}

func NewMockDynamoDBClient

func NewMockDynamoDBClient() *MockDynamoDBClient

NewMockDynamoDBClient creates a new mock DynamoDB client for testing.

func (*MockDynamoDBClient) BatchWriteItem

func (m *MockDynamoDBClient) BatchWriteItem(_ context.Context, params *dynamodb.BatchWriteItemInput, _ ...func(*dynamodb.Options)) (*dynamodb.BatchWriteItemOutput, error)

BatchWriteItem performs batch write operations.

func (*MockDynamoDBClient) ClearTables

func (m *MockDynamoDBClient) ClearTables()

ClearTables removes all data from the mock tables.

func (*MockDynamoDBClient) DeleteItem

func (m *MockDynamoDBClient) DeleteItem(_ context.Context, params *dynamodb.DeleteItemInput, _ ...func(*dynamodb.Options)) (*dynamodb.DeleteItemOutput, error)

DeleteItem removes an item from the mock table.

func (*MockDynamoDBClient) GetItem

func (m *MockDynamoDBClient) GetItem(_ context.Context, params *dynamodb.GetItemInput, _ ...func(*dynamodb.Options)) (*dynamodb.GetItemOutput, error)

GetItem retrieves an item from the mock table.

func (*MockDynamoDBClient) PutItem

func (m *MockDynamoDBClient) PutItem(_ context.Context, params *dynamodb.PutItemInput, _ ...func(*dynamodb.Options)) (*dynamodb.PutItemOutput, error)

PutItem stores an item in the mock table.

func (*MockDynamoDBClient) Query

func (m *MockDynamoDBClient) Query(_ context.Context, params *dynamodb.QueryInput, _ ...func(*dynamodb.Options)) (*dynamodb.QueryOutput, error)

Query searches for items in the mock table.

func (*MockDynamoDBClient) ResetCallCounts

func (m *MockDynamoDBClient) ResetCallCounts()

ResetCallCounts resets all call counters to zero.

func (*MockDynamoDBClient) Scan

func (m *MockDynamoDBClient) Scan(_ context.Context, params *dynamodb.ScanInput, _ ...func(*dynamodb.Options)) (*dynamodb.ScanOutput, error)

Scan scans all items in the mock table.

func (*MockDynamoDBClient) UpdateItem

func (m *MockDynamoDBClient) UpdateItem(_ context.Context, params *dynamodb.UpdateItemInput, _ ...func(*dynamodb.Options)) (*dynamodb.UpdateItemOutput, error)

UpdateItem updates an item in the mock table.

type SecretsRepository

SecretsRepository implements the database.SecretsRepository interface using DynamoDB.

type SecretsRepository struct {
    // contains filtered or unexported fields
}

func NewSecretsRepository

func NewSecretsRepository(client Client, tableName string, log *slog.Logger) *SecretsRepository

NewSecretsRepository creates a new DynamoDB-backed secrets repository.

func (*SecretsRepository) CreateSecret

func (r *SecretsRepository) CreateSecret(ctx context.Context, secret *api.Secret) error

CreateSecret stores a new secret's metadata in DynamoDB.

func (*SecretsRepository) DeleteSecret

func (r *SecretsRepository) DeleteSecret(ctx context.Context, name string) error

DeleteSecret removes a secret's metadata from DynamoDB.

func (*SecretsRepository) GetSecret

func (r *SecretsRepository) GetSecret(ctx context.Context, name string) (*api.Secret, error)

GetSecret retrieves a secret's metadata by name from DynamoDB.

func (*SecretsRepository) GetSecretsByRequestID

func (r *SecretsRepository) GetSecretsByRequestID(ctx context.Context, requestID string) ([]*api.Secret, error)

GetSecretsByRequestID retrieves all secrets created or modified by a specific request ID.

func (*SecretsRepository) ListSecrets

func (r *SecretsRepository) ListSecrets(ctx context.Context) ([]*api.Secret, error)

ListSecrets retrieves all secrets.

func (*SecretsRepository) SecretExists

func (r *SecretsRepository) SecretExists(ctx context.Context, name string) (bool, error)

SecretExists checks if a secret with the given name exists in DynamoDB.

func (*SecretsRepository) UpdateSecretMetadata

func (r *SecretsRepository) UpdateSecretMetadata(ctx context.Context, name, keyName, description, updatedBy string) error

UpdateSecretMetadata updates a secret's metadata (description and keyName) in DynamoDB.

type TokenRepository

TokenRepository implements the database.TokenRepository interface using DynamoDB.

type TokenRepository struct {
    // contains filtered or unexported fields
}

func (*TokenRepository) CreateToken

func (r *TokenRepository) CreateToken(ctx context.Context, token *api.WebSocketToken) error

CreateToken stores a new WebSocket authentication token with metadata.

func (*TokenRepository) DeleteToken

func (r *TokenRepository) DeleteToken(ctx context.Context, tokenValue string) error

DeleteToken removes a token from the database.

func (*TokenRepository) GetToken

func (r *TokenRepository) GetToken(ctx context.Context, tokenValue string) (*api.WebSocketToken, error)

GetToken retrieves a token by its value. Returns nil if the token doesn't exist (DynamoDB TTL automatically removes expired tokens).

type UserRepository

UserRepository implements the database.UserRepository interface using DynamoDB.

type UserRepository struct {
    // contains filtered or unexported fields
}

func NewUserRepository

func NewUserRepository(client Client, tableName string, pendingTableName string, log *slog.Logger) *UserRepository

NewUserRepository creates a new DynamoDB-backed user repository.

func (*UserRepository) CreatePendingAPIKey

func (r *UserRepository) CreatePendingAPIKey(ctx context.Context, pending *api.PendingAPIKey) error

CreatePendingAPIKey stores a pending API key with a secret token.

func (*UserRepository) CreateUser

func (r *UserRepository) CreateUser(ctx context.Context, user *api.User, apiKeyHash string, expiresAtUnix int64) error

CreateUser stores a new user with their hashed API key in DynamoDB. If expiresAtUnix is 0, no TTL is set (permanent user). If expiresAtUnix is > 0, it sets the expires_at field for automatic deletion.

func (*UserRepository) DeletePendingAPIKey

func (r *UserRepository) DeletePendingAPIKey(ctx context.Context, secretToken string) error

DeletePendingAPIKey removes a pending API key from the database.

func (*UserRepository) GetPendingAPIKey

func (r *UserRepository) GetPendingAPIKey(ctx context.Context, secretToken string) (*api.PendingAPIKey, error)

GetPendingAPIKey retrieves a pending API key by its secret token.

func (*UserRepository) GetUserByAPIKeyHash

func (r *UserRepository) GetUserByAPIKeyHash(ctx context.Context, apiKeyHash string) (*api.User, error)

GetUserByAPIKeyHash retrieves a user by their hashed API key (primary key).

func (*UserRepository) GetUserByEmail

func (r *UserRepository) GetUserByEmail(ctx context.Context, email string) (*api.User, error)

GetUserByEmail retrieves a user by their email using the GSI.

func (*UserRepository) GetUsersByRequestID

func (r *UserRepository) GetUsersByRequestID(ctx context.Context, requestID string) ([]*api.User, error)

GetUsersByRequestID retrieves all users created or modified by a specific request ID.

func (*UserRepository) ListUsers

func (r *UserRepository) ListUsers(ctx context.Context) ([]*api.User, error)

ListUsers returns all users in the system sorted by email (excluding API key hashes for security). Uses the all-user_email GSI to retrieve users in sorted order directly from DynamoDB.

func (*UserRepository) MarkAsViewed

func (r *UserRepository) MarkAsViewed(ctx context.Context, secretToken, ipAddress string) error

MarkAsViewed atomically marks a pending key as viewed with the IP address.

func (*UserRepository) RemoveExpiration

func (r *UserRepository) RemoveExpiration(ctx context.Context, email string) error

RemoveExpiration removes the expires_at field from a user record, making them permanent.

func (*UserRepository) RevokeUser

func (r *UserRepository) RevokeUser(ctx context.Context, email string) error

RevokeUser marks a user's API key as revoked.

func (*UserRepository) UpdateLastUsed

func (r *UserRepository) UpdateLastUsed(ctx context.Context, email string) (*time.Time, error)

UpdateLastUsed updates the last_used timestamp for a user.

Generated by gomarkdoc