Matrix logo

Deus - Hosting Backends and Budget Controls

Appwrite-backed hosted execution: artifact packaging, function provisioning, variable injection, deployment lifecycle, and kill-switch/warm-capacity budget gates.

Overview

The hosting layer provisions, updates, and removes Appwrite-backed function deployments. A budget gate decides whether a new allocation can proceed based on kill-switch state and always-warm capacity limits.

Hosting Contract

Source: deus/internal/hosting/backend.go

DeployInput

PropertyTypeDescription
ServiceIDstringService identifier
ArtifactKeystringObject storage key for the uploaded artifact
RuntimestringRuntime selector (currently node20)
AlwaysWarmboolRequests dedicated warm resource profile
RegionstringRegion selector
FunctionNamestringOptional explicit function name
Envmap[string]stringPer-service environment variables

DeployResult

PropertyTypeDescription
FunctionIDstringAppwrite function identifier
DeploymentIDstringAppwrite deployment identifier
ExecEndpointstringSynchronous execution URL

Backend interface

type Backend interface {
    Deploy(ctx context.Context, in DeployInput) (DeployResult, error)
    Delete(ctx context.Context, functionID string) error
}

Appwrite Backend

Source: deus/internal/hosting/appwrite.go

The Appwrite backend validates configuration, reads the artifact from object storage, creates an Appwrite function, pushes variables, uploads the code bundle, and returns the execution endpoint.

Deploy flow

  1. Validate Endpoint, ProjectID, APIKey are set
  2. Load artifact bytes from BlobReader.Get
  3. Package as code.tar.gz (gzip passthrough if already gzipped)
  4. POST /functions with {name, runtime: "node-20.0", execute: ["any"], enabled: true, entrypoint: "src/main.js", commands: "npm install"}
  5. If AlwaysWarm, add specification: "s-1vcpu-512mb"
  6. POST /variables one at a time in deterministic key order
  7. POST /deployments as multipart form data with activate: true
  8. Return {FunctionID, DeploymentID, ExecEndpoint}

Key parameters

ParameterValue
HTTP timeout120 seconds
Default function timeout30000ms (clamped to at least 1s)
Max response bytes262144 (pushed as DEUS_MAX_RESPONSE_BYTES)
Warm specifications-1vcpu-512mb
Artifact namecode.tar.gz

Hosted service HTTP routes

MethodPathAuthDescription
POST/v1/services/{id}/artifactsDeveloper (owner)Upload artifact (multipart, 12 MB max)
POST/v1/services/{id}/deployDeveloper (owner)Deploy to Appwrite
POST/v1/services/{id}/redeployDeveloper (owner)Redeploy service
GET/v1/services/{id}/deploymentsDeveloper (owner)List deployments
GET/v1/services/{id}/deployments/{did}Developer (owner)Get deployment detail

Orchestrator

The Orchestrator ties the Backend, Budget, and Store together. The Deploy method:

  1. Checks budget via Budget.AllowNewDeployment(alwaysWarm)
  2. Calls Backend.Deploy
  3. Records the deployment in the store

Budget Controls

Source: deus/internal/hosting/budget.go

Budget gate flow

AllowNewDeployment(alwaysWarm)
  ├── alwaysWarm=true → AllowAlwaysWarm
  │     ├── KillSwitch active? → reject
  │     ├── CountAlwaysWarmDeployments >= MaxAlwaysWarm? → reject
  │     └── allow
  └── alwaysWarm=false
        ├── KillSwitch active? → reject
        └── allow

The kill-switch short-circuits before any store lookup. BudgetWei parses limits.BudgetPAXWei as base-10 text.

Error cases

ConditionError
Kill switch active (all deploys)hosting_budget_exceeded
Kill switch active (always-warm)hosting_budget_exceeded
Always-warm capacity exhaustedhosting_budget_exceeded

Handler error mapping

Error patternHTTP statusCode
not found404not_found
budget, kill-switch409hosting_budget_exceeded
not hosted400invalid_request
Other500internal_error