# Guara Cloud > Guara Cloud is a Brazilian Kubernetes-based Platform-as-a-Service (PaaS) that enables developers to deploy containerized applications with minimal infrastructure knowledge. Built on OCI OKE with ARM-based Ampere A1 instances in Sao Paulo (br-saopaulo-1). All billing is in BRL (Brazilian Real) via Stripe. - Website: https://guaracloud.com - Dashboard: https://app.guaracloud.com - API Base URL: https://api.guaracloud.com/v1 - Status Page: https://api.guaracloud.com/v1/status - Changelog: https://guaracloud.com/changelog --- ## Core Concepts ### User A registered user authenticated via GitHub or Google OAuth. Each user has exactly one subscription (tier) that governs resource limits across all their projects. Roles: `user` (default) or `admin` (platform operator). ### Project The primary organizational unit. A project maps 1:1 to an isolated Kubernetes namespace. Projects contain one or more services. Each project has its own resource quotas, environment variables, and access controls. Namespace format: `proj-{projectSlug}-{shortID}`. ### Service A deployable unit within a project. Each service corresponds to a single container, with its own subdomain, environment variables, scaling configuration, and deployment history. A project can contain multiple services (e.g., web frontend, API backend, worker). ### Deployment A single build-and-deploy cycle for a service. Deployments are triggered by git push (automatic), manual trigger, rollback, or config change. Each deployment tracks build status, image tag, commit SHA, duration, and errors. ### Domain Each service gets an automatic subdomain: `{service}-{project}.guaracloud.com`. Users can add up to 10 custom domains per service via CNAME verification. ### Project Members Projects support team collaboration with role-based access: `owner`, `admin`, `member`, `viewer`. --- ## Authentication ### OAuth Providers - GitHub: Scopes `read:user`, `user:email` - Google: Scopes `openid`, `profile`, `email` ### API Authentication All authenticated API requests require a Bearer token in the Authorization header: ``` Authorization: Bearer ``` ### JWT Token Claims ```json { "sub": "user_uuid", "jti": "token_uuid", "email": "user@example.com", "name": "User Name", "role": "user", "tier": "pro", "iat": 1700000000, "exp": 1700003600, "iss": "guaracloud.com" } ``` Token lifetime: 1 hour. Automatic refresh when expired. ### SSE Token For log streaming endpoints, a separate short-lived token (15 min) scoped to a specific project and service is required. Obtain via `POST /v1/auth/sse-token`. --- ## Rate Limiting - Authenticated users: 1,000 requests/min - Unauthenticated users: 30 requests/min - Response headers: `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` - Exceeded: HTTP 429 with error code `RATE_LIMITED` --- ## Error Response Format All errors follow this envelope: ```json { "statusCode": 404, "error": "NOT_FOUND", "message": "Project proj_abc not found", "details": {}, "timestamp": "2026-02-23T12:00:00.000Z", "path": "/v1/projects", "requestId": "550e8400-e29b-41d4-a716-446655440000" } ``` Error codes: - BAD_REQUEST (400) - VALIDATION_ERROR (400) - includes `details.errors` array - UNAUTHORIZED (401) - SUBSCRIPTION_REQUIRED (402) - FORBIDDEN (403) - NOT_FOUND (404) - CONFLICT (409) - UNPROCESSABLE_ENTITY (422) - RATE_LIMITED (429) - INTERNAL_ERROR (500) - SERVICE_UNAVAILABLE (503) --- ## Pagination List endpoints use query parameters `?page=1&limit=20`. Responses include a `meta` object: ```json { "data": [...], "meta": { "page": 1, "limit": 20, "total": 42, "total_pages": 3, "has_next": true, "has_previous": false } } ``` --- ## Pricing Tiers All prices in BRL (R$). Yearly plans offer 20% discount (charged upfront). ### Hobby (Free) - Price: R$0/month - Projects: 1 - Services per project: 2 - vCPU per service: 0.25 - RAM per service: 256 MB - Build minutes/month: 100 - Log retention: 1 day - Replicas per service: 1 - Persistent volumes: 1 (max 1 Gi each, 1 Gi total) - Custom domains: 10/service - Bandwidth: Unlimited - Overage: Not available - Support: Community ### Pro - Price: R$49/month (R$39/month yearly) - Projects: 5 - Services per project: 5 - vCPU per service: 1 - RAM per service: 1 GB - Build minutes/month: 500 - Log retention: 7 days - Replicas per service: 3 - Persistent volumes: 5 (max 5 Gi each, 10 Gi total) - Custom domains: 10/service - Bandwidth: Unlimited - Overage: Opt-in per project - Support: Email (48h SLA) ### Business - Price: R$199/month (R$159/month yearly) - Projects: 20 - Services per project: 15 - vCPU per service: 2 - RAM per service: 4 GB - Build minutes/month: 2,000 - Log retention: 30 days - Replicas per service: 10 - Persistent volumes: 10 (max 10 Gi each, 50 Gi total) - Custom domains: 10/service - Bandwidth: Unlimited - Overage: Opt-in per project - Support: Priority (24h SLA) ### Enterprise - Price: Custom (negotiated) - Projects: Unlimited - Services per project: Unlimited - vCPU per service: 4 - RAM per service: 8 GB - Build minutes/month: 10,000 - Log retention: 30 days - Replicas per service: 50 - Persistent volumes: 50 (max 50 Gi each, 500 Gi total) - Custom domains: 10/service - Bandwidth: Unlimited - Overage: Included - Support: Dedicated (4h SLA) ### Overage Rates (Pro/Business opt-in, Enterprise included) - Extra vCPU: R$40/month (prorated hourly) - Extra RAM: R$20/GB/month (prorated hourly) - Extra build minutes: R$0.10/minute - Extra storage: R$5/GB/month (prorated daily) --- ## API Endpoints ### Auth GET /v1/auth/me Returns authenticated user profile with tier and role. Response: { data: { id, email, name, avatar_url, role, tier, created_at } } POST /v1/auth/logout Invalidates current JWT. Returns 204. POST /v1/auth/refresh Issues fresh JWT for non-browser clients. Response: { data: { token } } POST /v1/auth/sse-token Issues 15-minute SSE token scoped to project and service. Body: { project_id: string, service_id: string } Response: { data: { token, expires_at } } PATCH /v1/account/profile Updates user profile. Body: { name: string } DELETE /v1/account Permanently deletes user account, cancels subscriptions, deletes all projects. Returns 204. ### Projects GET /v1/projects Lists all projects where user is a member. Query: ?page=1&limit=20&role=owner Response: { data: [ProjectResponse], meta: PaginationMeta } POST /v1/projects Creates a new project. Validates tier limits. Body: { name: string, description?: string } Response (201): { data: ProjectResponse } GET /v1/projects/:id Returns full project details. Response: { data: ProjectResponse } PATCH /v1/projects/:id Updates project metadata. Body: { name?: string, description?: string } Required role: owner or admin DELETE /v1/projects/:id Deletes project and all its services. Required role: owner #### ProjectResponse ```json { "id": "proj_xyz789", "name": "my-saas-app", "slug": "my-saas-app", "description": "Production SaaS application", "namespace": "proj-my-saas-app-a1b2c3d4", "status": "provisioning | active | suspended | deleting | deleted", "tier": "hobby | pro | business | enterprise", "deploy_region": "sa-saopaulo-1", "owner_id": "usr_abc123", "user_role": "owner | admin | member | viewer", "member_count": 1, "service_count": 0, "created_at": "2026-02-20T14:00:00Z", "updated_at": "2026-02-20T14:00:00Z" } ``` ### Project Members GET /v1/projects/:projectId/members Lists all members with roles and profile info. POST /v1/projects/:projectId/members Invites user by email. Body: { email: string, role: "admin" | "member" | "viewer" } Required role: owner or admin PATCH /v1/projects/:projectId/members/:memberId Changes member role. Body: { role: "admin" | "member" | "viewer" } Required role: owner or admin DELETE /v1/projects/:projectId/members/:memberId Removes member from project. Required role: owner or admin POST /v1/projects/:projectId/members/leave Allows authenticated user to leave project (owner cannot leave). POST /v1/projects/:projectId/transfer-ownership Transfers project ownership to another member. Body: { new_owner_id: string } Required role: owner ### Services GET /v1/projects/:projectId/services Lists all services in project. POST /v1/projects/:projectId/services Creates a new service. Does NOT trigger a build. Body: { name: string, github_repo_url?: string, build_method: "dockerfile" | "buildpack" | "image", image_reference?: string, port: number, env_vars?: Record, build_env_keys?: string[] } The optional `build_env_keys` array lists environment variable keys that should be made available as Docker build args during image builds. Keys listed here must exist in `env_vars`. Required role: owner, admin, or member GET /v1/projects/:projectId/services/:id Returns full service details including deployment status, resources, health. PATCH /v1/projects/:projectId/services/:id Updates service configuration. Body: { name?, port?, build_method?, github_repo_url? } DELETE /v1/projects/:projectId/services/:id Deletes service and all associated resources. POST /v1/projects/:projectId/services/:id/restart Rolling restart of service pods. POST /v1/projects/:projectId/services/:id/stop Scales service to zero replicas. POST /v1/projects/:projectId/services/:id/start Scales service back to configured replica count. PATCH /v1/projects/:projectId/services/:id/scaling Updates scaling and resource allocation. Body: { replicas?: number, cpu_request?: string, cpu_limit?: string, memory_request?: string, memory_limit?: string } GET /v1/projects/:projectId/services/:id/env Returns service environment variables (secret values masked). Response: { data: [{ key: string, value: string, availableAtBuild?: boolean }, ...] } The `availableAtBuild` field indicates whether the variable is passed as a Docker build arg during image builds. PUT /v1/projects/:projectId/services/:id/env Replaces all environment variables. Triggers rolling update if service is running. Body: { env_vars: [{ key: string, value: string, availableAtBuild?: boolean }, ...] } Each entry may include `availableAtBuild: true` to mark the variable as a build-time arg. Variables without the field (or set to false) are runtime-only. #### ServiceResponse ```json { "id": "svc_abc123", "project_id": "proj_xyz789", "name": "api-backend", "slug": "api-backend", "github_repo_url": "https://github.com/user/my-api", "github_connection_stale": false, "build_method": "dockerfile | buildpack | image", "image_reference": null, "port": 3000, "status": "creating | building | deploying | running | stopped | failed | deleting | deleted", "subdomain": "api-backend-my-saas-app.guaracloud.com", "url": "https://api-backend-my-saas-app.guaracloud.com", "replicas": { "ready": 2, "desired": 2 }, "resources": { "cpu_request": "250m", "cpu_limit": "1", "memory_request": "512Mi", "memory_limit": "1Gi" }, "current_deployment_id": "dep_xyz", "health_status": "healthy | crash_loop | image_pull_error | oom_killed | degraded | unknown", "created_at": "2026-02-20T14:00:00Z", "updated_at": "2026-02-20T14:00:00Z" } ``` ### Deployments GET /v1/projects/:projectId/services/:serviceId/deployments Lists deployments, newest first. POST /v1/projects/:projectId/services/:serviceId/deployments Triggers a new deployment. Body (git-based): { branch?: string, commit_sha?: string } Body (image-based): { image: string } Response (201): { data: DeploymentResponse } GET /v1/projects/:projectId/services/:serviceId/deployments/:id Returns full deployment details. POST /v1/projects/:projectId/services/:serviceId/deployments/:id/rollback Rolls back to a historical deployment by creating a new deployment with the historical image. GET /v1/projects/:projectId/services/:serviceId/deployments/:id/logs SSE stream of build/deploy logs. Requires SSE token. #### DeploymentResponse ```json { "id": "dep_abc123", "service_id": "svc_abc123", "project_id": "proj_xyz789", "status": "pending | building | deploying | healthy | failed | rolled_back | superseded", "image_tag": "harbor.guaracloud.com/proj/svc:abc1234", "commit_sha": "a1b2c3d4", "branch": "main", "build_method": "dockerfile | buildpack | image", "build_duration_seconds": 45, "deploy_duration_seconds": 30, "url": "https://api-backend-my-saas-app.guaracloud.com", "error_message": null, "trigger": "push | manual | rollback | config_change", "created_at": "2026-02-20T14:00:00Z", "updated_at": "2026-02-20T14:00:00Z" } ``` ### Domains GET /v1/projects/:projectId/services/:serviceId/domains Lists all domains (auto-assigned subdomain + custom domains). POST /v1/projects/:projectId/services/:serviceId/domains Adds a custom domain. Returns DNS instructions for verification. Body: { domain: string } Constraints: CNAME subdomains only (no apex/naked domains), max 10 per service. Response (201): { data: DomainResponse } DELETE /v1/projects/:projectId/services/:serviceId/domains/:id Removes a custom domain. POST /v1/projects/:projectId/services/:serviceId/domains/:id/verify Verifies domain ownership via TXT record lookup. #### DomainResponse ```json { "id": "dom_abc123", "domain": "app.example.com", "type": "subdomain | custom", "status": "pending_verification | verified | failed", "verification_token": "guara-verify=f47ac10b58cc4372", "dns_instructions": { "txt_record": { "host": "_guara-verify.app.example.com", "value": "guara-verify=f47ac10b58cc4372" }, "routing_record": { "type": "CNAME", "host": "app.example.com", "value": "ingress.guaracloud.com" } }, "created_at": "2026-02-20T14:30:00Z" } ``` ### Billing GET /v1/billing/subscription Returns current subscription details: tier, status, period dates, payment method. POST /v1/billing/activate-free Activates free Hobby tier instantly. No Stripe required. POST /v1/billing/checkout Creates Stripe Checkout Session for initial paid subscription. Body: { tier: "pro" | "business" | "enterprise", interval?: "month" | "year" } Response (201): { data: { checkout_url, session_id } } POST /v1/billing/subscribe Changes subscription tier (upgrade/downgrade). Body: { tier: "pro" | "business" | "enterprise" } Upgrades: immediate with proration. Downgrades: effective at period end. POST /v1/billing/cancel Cancels subscription at end of current billing period. GET /v1/billing/invoices Lists invoices with pagination. GET /v1/billing/invoices/:id Returns detailed invoice with line items. GET /v1/billing/usage Returns current period usage vs tier limits. Response: { data: { period_start, period_end, bandwidth, build_minutes, vcpu_hours, ram_gb_hours, estimated_overage_brl } } POST /v1/billing/portal Generates Stripe Billing Portal URL for managing payment methods. PATCH /v1/billing/overage Enables/disables overage billing for a project. Body: { project_id: string, enabled: boolean } Available on Pro/Business tiers only. ### GitHub Integration GET /v1/github/installations Lists user's GitHub App installations. POST /v1/github/installations/confirm Associates GitHub App installation with user after redirect. Body: { installation_id: number } GET /v1/github/repos Lists repositories accessible via installations. Query: ?installation_id=123&search=my-repo POST /v1/github/connect Connects a GitHub repo to a service and enables auto-deploy on push. Body: { service_id: string, installation_id: number, repo_full_name: string, branch: string } DELETE /v1/github/disconnect/:serviceId Disconnects GitHub repo from service. ### Logs & Metrics GET /v1/projects/:projectId/services/:serviceId/logs SSE stream of runtime logs. Requires SSE token. Query: ?since=1h&tail=100 GET /v1/projects/:projectId/services/:serviceId/metrics Returns current metrics: CPU, memory, network, replica count. GET /v1/projects/:projectId/services/:serviceId/metrics/history Returns historical metrics time series. Query: ?from=ISO8601&to=ISO8601&step=60 Metrics time range limits by tier: Hobby 1h, Pro 24h, Business 7d, Enterprise 30d. ### Notifications GET /v1/notifications Lists user notifications. Query: ?page=1&limit=20&is_read=false GET /v1/notifications/unread-count Returns unread notification count. PATCH /v1/notifications/:id/read Marks notification as read. PATCH /v1/notifications/read-all Marks all notifications as read. ### Health & Status GET /v1/health/live Liveness probe. Returns 200 if process alive. GET /v1/health/ready Readiness probe. Returns 200 if database and message queue are connected. GET /v1/status Public status page with component statuses and active incidents. --- ## Deployment Pipeline ### Build Methods - dockerfile: BuildKit builder (when Dockerfile present in repo) - buildpack: Buildpacks (auto-detects language/framework, no Dockerfile needed) - image: Direct image deployment from any registry (no build step) ### Deployment Triggers - push: Automatic on git push to configured branch (via GitHub webhook) - manual: User triggers via API or dashboard - rollback: User rolls back to a previous deployment - config_change: Environment variable or resource configuration change ### Deployment Status Lifecycle ``` pending -> building -> deploying -> healthy -> failed -> rolled_back building -> failed (build error) pending -> superseded (newer deployment started) ``` ### Automatic Subdomain Default pattern: `{service_slug}-{project_slug}.guaracloud.com` If collision: `{service_slug}-{project_slug}-{salt}.guaracloud.com` Subdomains are immutable once created. ### Custom Domain Setup 1. Add domain: POST /v1/projects/:projectId/services/:serviceId/domains { domain: "app.example.com" } 2. Create DNS records: - TXT record: `_guara-verify.app.example.com` -> `guara-verify=` - CNAME record: `app.example.com` -> `ingress.guaracloud.com` 3. Verify: POST /v1/projects/:projectId/services/:serviceId/domains/:id/verify 4. TLS certificate auto-provisioned via Let's Encrypt after verification. Constraints: CNAME subdomains only. Apex/naked domains not supported. --- ## Environment Variables ### User-Defined Set via PUT /v1/projects/:projectId/services/:id/env with an array of variable entries. - Max 500 env vars per service - Values up to 2KB each - Encrypted at rest, decrypted at deployment time - Updating env vars triggers a rolling restart Each environment variable entry has the shape: ```json { "key": "DATABASE_URL", "value": "postgres://...", "availableAtBuild": false } ``` The `availableAtBuild` field (optional, defaults to `false`) controls whether the variable is injected as a Docker build arg (`--build-arg`) during image builds. This is useful for variables needed at build time, such as private registry tokens (`NPM_TOKEN`), framework build flags, or API keys consumed by static site generators during the build step. Build-time variables are also available at runtime — marking a variable as `availableAtBuild` does not remove it from the runtime environment. When creating a service via POST /v1/projects/:projectId/services, you can specify `build_env_keys: string[]` in the request body to designate which of the initial `env_vars` keys should be marked as build-time variables. ### Platform-Injected (automatic) These are always available in every service container: - GUARA_PROJECT_ID: Project UUID - GUARA_SERVICE_ID: Service UUID - GUARA_SERVICE_NAME: Service slug - GUARA_ENVIRONMENT: "production" - PORT: Container port (if not set by user) --- ## RBAC Roles | Permission | owner | admin | member | viewer | |------------------------|-------|-------|--------|--------| | View project | yes | yes | yes | yes | | Create service | yes | yes | yes | no | | Deploy | yes | yes | yes | no | | Manage env vars | yes | yes | yes | no | | Invite members | yes | yes | no | no | | Change member roles | yes | yes | no | no | | Delete project | yes | no | no | no | | Transfer ownership | yes | no | no | no | --- ## Common Workflows ### Deploy a new application from GitHub 1. POST /v1/projects { name: "my-app" } 2. POST /v1/billing/activate-free (if no subscription) 3. GET /v1/github/installations (get installation_id) 4. GET /v1/github/repos?installation_id=123 (find repo) 5. POST /v1/projects/:projectId/services { name: "web", github_repo_url: "https://github.com/user/repo", build_method: "dockerfile", port: 3000 } 6. POST /v1/github/connect { service_id: "svc_xxx", installation_id: 123, repo_full_name: "user/repo", branch: "main" } 7. POST /v1/projects/:projectId/services/:serviceId/deployments { branch: "main" } 8. GET /v1/projects/:projectId/services/:serviceId/deployments/:id (poll for status) ### Add a custom domain 1. POST /v1/projects/:projectId/services/:serviceId/domains { domain: "app.example.com" } 2. User creates DNS records (TXT for verification + CNAME for routing) 3. POST /v1/projects/:projectId/services/:serviceId/domains/:id/verify 4. TLS provisioned automatically ### Scale a service 1. PATCH /v1/projects/:projectId/services/:id/scaling { replicas: 3, cpu_limit: "1", memory_limit: "1Gi" } ### Set environment variables 1. PUT /v1/projects/:projectId/services/:id/env { env_vars: [{ "key": "DATABASE_URL", "value": "postgres://..." }, { "key": "NODE_ENV", "value": "production" }, { "key": "NPM_TOKEN", "value": "xxx", "availableAtBuild": true }] } ### Upgrade subscription tier 1. POST /v1/billing/subscribe { tier: "pro" } Upgrade is immediate with proration; downgrade takes effect at period end. ### Rollback a deployment 1. GET /v1/projects/:projectId/services/:serviceId/deployments (find the deployment to rollback to) 2. POST /v1/projects/:projectId/services/:serviceId/deployments/:id/rollback ### Monitor a service 1. GET /v1/projects/:projectId/services/:serviceId/metrics (current CPU, memory, network) 2. GET /v1/projects/:projectId/services/:serviceId/metrics/history?from=...&to=...&step=60 --- ## Supported Languages & Frameworks (via Buildpacks auto-detection) When using build_method: "buildpack", the platform auto-detects: - Node.js (npm, yarn, pnpm) - Python (pip, pipenv, poetry) - Go - Java (Maven, Gradle) - Ruby (Bundler) - PHP (Composer) - .NET - Rust - Static sites (HTML/CSS/JS) For other languages or custom builds, use build_method: "dockerfile" with your own Dockerfile. --- ## Billing Lifecycle ### Subscription States - active: Billing normally - past_due: Payment failed, grace period (10 days) - suspended: Unpaid >10 days, services scaled to zero (not deleted) - canceled: User cancelled, runs until period end ### Payment Failure Timeline - Day 1: Payment fails -> status past_due, email notification - Day 10: Still unpaid -> status suspended, services scaled to zero - Day 38: Still unpaid -> project deletion initiated - Recovery: Update payment method -> services restart on next successful payment --- ## Notes for AI Agents - All responses use Content-Type: application/json - Field naming: snake_case in responses, camelCase in request bodies - Subdomains are immutable once created - Tier limits are enforced server-side (not via JWT claims) - Environment variables are encrypted at rest - SSE streams require a dedicated SSE token (not the main JWT) - Webhook endpoints (GitHub, Stripe) use signature verification, not Bearer tokens - The Hobby tier requires no payment method and can be activated instantly - Bandwidth is unlimited on all tiers (fair-use monitored internally) --- ## CLI (`guara`) The GuaraCloud CLI (`guara`) is the primary interface for AI agents and developers to manage projects, services, deployments, environment variables, domains, and scaling from the terminal. It is built on oclif and supports three output modes designed for both human and machine consumption. ### Installation & Authentication ```bash # Install globally via npm npm install -g @guaracloud/cli # Authenticate with an API key (recommended for AI agents / CI) export GUARA_API_KEY=gk_live_xxxxx # or guara login --api-key gk_live_xxxxx # Authenticate via browser OAuth (interactive) guara login guara login --browser # Verify authentication guara whoami --json ``` API keys are stored in the system keychain (macOS Keychain, libsecret on Linux) when available, falling back to `~/.config/guara/config.json`. ### Configuration Override the API base URL for staging or local development: ```bash # Via flag (per-command) guara projects list --api-url https://api.staging.guaracloud.com # Via environment variable (session-wide) export GUARA_API_URL=https://api.staging.guaracloud.com # Via persistent config guara config set apiUrl https://api.staging.guaracloud.com ``` ### Output Modes Every command supports three output modes. AI agents should always use `--json`. **Default (human-readable):** Formatted tables, colors, spinners. ```bash guara services list ``` **`--json` (machine-readable):** Structured JSON to stdout. Errors go to stderr as JSON. ```bash guara services list --json # stdout: { "data": [...] } ``` **`--quiet` (`-q`):** Minimal output — a single ID, slug, or newline-separated list of slugs. Ideal for piping. ```bash guara projects list --quiet # stdout: my-project\nother-project ``` ### JSON Response Envelope All `--json` output follows this pattern: Success: ```json { "data": } ``` Error (to stderr): ```json { "error": { "code": "SERVICE_NOT_FOUND", "message": "Service not found or you do not have access.", "status": 404 } } ``` ### Exit Codes - `0` — Success - `1` — General error (bad input, API 4xx/5xx, resource not found) - `2` — Authentication error (invalid/expired API key, 401) ### Global Flags Every command inherits these flags: | Flag | Short | Env Var | Description | |------|-------|---------|-------------| | `--json` | | | Output as JSON | | `--quiet` | `-q` | | Suppress non-essential output | | `--api-key ` | | `GUARA_API_KEY` | Override API key for this request | | `--api-url ` | | `GUARA_API_URL` | Override API base URL | | `--project ` | `-p` | `GUARA_PROJECT` | Project slug | | `--service ` | `-s` | `GUARA_SERVICE` | Service slug | | `--yes` | `-y` | | Skip confirmation prompts | ### Context Resolution Commands that require a project or service resolve context in this order: **Project:** 1. `--project` flag 2. `GUARA_PROJECT` environment variable 3. `.guara.json` file (walked upward from cwd) **Service:** 1. `--service` flag 2. `GUARA_SERVICE` environment variable 3. `.guara.json` file `service` field The `.guara.json` link file format: ```json { "project": "my-project", "service": "my-api" } ``` Create it with `guara link`. Remove it with `guara unlink`. --- ### Command Reference #### Auth Commands ##### `guara login` Authenticate with GuaraCloud. | Flag | Short | Description | |------|-------|-------------| | `--api-key ` | | Authenticate directly with an API key (also reads `GUARA_API_KEY`) | | `--browser` | | Force browser-based OAuth login | ```bash # AI agent authentication (non-interactive) guara login --api-key gk_live_xxxxx --json ``` JSON response: `{ "data": { "authenticated": true, "method": "api-key", "storage": "keychain", "email": "user@example.com" } }` Quiet response: `user@example.com` ##### `guara logout` Log out and remove stored credentials. ```bash guara logout --json ``` JSON response: `{ "data": { "loggedOut": true, "credentialsRemoved": true } }` Quiet response: `logged_out` ##### `guara whoami` Show the currently authenticated user. ```bash guara whoami --json ``` JSON response: `{ "data": { "id": "usr_abc", "email": "user@example.com", "name": "User", "tier": "pro", "role": "user", "isFounder": false } }` Quiet response: `user@example.com` --- #### Project Commands ##### `guara projects list` List all projects in your account. ```bash guara projects list --json ``` JSON response: `{ "data": [ProjectResponse, ...] }` Quiet response: one slug per line ##### `guara projects create` Create a new project. | Flag | Short | Description | |------|-------|-------------| | `--name ` | `-n` | Project name (required in `--json`/`--quiet` mode) | | `--description ` | `-d` | Project description | | `--region ` | `-r` | Deploy region (default: `br-gru`, options: `br-gru`) | ```bash guara projects create --name my-project --json ``` JSON response: `{ "data": ProjectResponse }` Quiet response: `my-project` (the slug) ##### `guara projects info` Show details of a project. Requires project context. ```bash guara projects info --project my-project --json ``` JSON response: `{ "data": ProjectResponse }` Quiet response: the project slug --- #### Service Commands ##### `guara services list` List all services in a project. Requires project context. ```bash guara services list --project my-project --json ``` JSON response: `{ "data": [ServiceResponse, ...] }` Quiet response: one slug per line ##### `guara services create` Create a new service in a project. Requires project context. | Flag | Short | Description | |------|-------|-------------| | `--name ` | `-n` | Service name (required in `--json`/`--quiet` mode) | | `--build-method ` | `-b` | Build method: `dockerfile` or `buildpack` (required in `--json`/`--quiet` mode) | | `--repo ` | | GitHub repository URL | | `--branch ` | | Git branch (default: main) | | `--port ` | | Container port (required in `--json`/`--quiet` mode) | | `--root-dir ` | | Root directory for the build (default: `.`) | | `--public` | | Make service publicly accessible (default: false) | ```bash guara services create --name my-api --build-method dockerfile --port 3000 --project my-project --json ``` JSON response: `{ "data": ServiceResponse }` Quiet response: the service slug ##### `guara services info` Show details of a service. Requires project + service context. ```bash guara services info --project my-project --service my-api --json ``` JSON response: `{ "data": ServiceResponse }` Quiet response: the service slug ##### `guara services delete` Delete a service. Requires project + service context. Requires `--yes` in non-interactive mode. ```bash guara services delete --project my-project --service my-api --yes --json ``` JSON response: `{ "data": { "project": "my-project", "service": "my-api", "action": "delete" } }` Quiet response: the service slug ##### `guara services restart` Trigger a rolling restart of a service. Requires project + service context. ```bash guara services restart --project my-project --service my-api --json ``` JSON response: `{ "data": { "project": "my-project", "service": "my-api", "action": "restart" } }` Quiet response: the service slug ##### `guara services start` Start a stopped service. Requires project + service context. ```bash guara services start --project my-project --service my-api --json ``` JSON response: `{ "data": { "project": "my-project", "service": "my-api", "action": "start" } }` Quiet response: the service slug ##### `guara services stop` Stop a service (scale to zero). Requires project + service context. ```bash guara services stop --project my-project --service my-api --json ``` JSON response: `{ "data": { "project": "my-project", "service": "my-api", "action": "stop" } }` Quiet response: the service slug --- #### Deployment Commands ##### `guara deploy` Trigger a new deployment for a service. Requires project + service context. Polls until the deployment reaches a terminal state (healthy, failed, rolled_back, superseded) with a 10-minute timeout. | Flag | Short | Description | |------|-------|-------------| | `--branch ` | `-b` | Git branch to deploy | | `--commit ` | `-c` | Specific commit SHA to deploy | ```bash guara deploy --branch main --project my-project --service my-api --json ``` JSON response: `{ "data": DeploymentResponse }` (final state after polling) Quiet response: the deployment ID (returned immediately, no polling) ##### `guara deployments list` List deployment history for a service. Requires project + service context. ```bash guara deployments list --project my-project --service my-api --json ``` JSON response: `{ "data": [DeploymentResponse, ...] }` Quiet response: one deployment ID per line ##### `guara rollback` Rollback to a previous deployment. Requires project + service context. | Flag | Short | Description | |------|-------|-------------| | `--deployment ` | `-d` | Deployment ID to rollback to (required in `--json`/`--quiet` mode) | ```bash guara rollback --deployment dep_abc123 --project my-project --service my-api --json ``` JSON response: `{ "data": DeploymentResponse }` (the new rollback deployment) Quiet response: the new deployment ID --- #### Environment Variable Commands ##### `guara env list` List environment variables for a service. Requires project + service context. The default table output includes a "Build" column showing whether each variable is available at build time. ```bash guara env list --project my-project --service my-api --json ``` JSON response: `{ "data": [{ "key": "DATABASE_URL", "value": "postgres://...", "availableAtBuild": false }, ...] }` Quiet response: one key per line ##### `guara env set` Set environment variables (merges with existing). Triggers a rolling restart. Requires project + service context. Accepts variadic `KEY=value` arguments. | Flag | Short | Description | |------|-------|-------------| | `--from-file ` | | Read KEY=VALUE pairs from a `.env` file | | `--build` | `-b` | Mark the variables being set as available at build time (Docker build args) | ```bash guara env set DATABASE_URL=postgres://host/db NODE_ENV=production --project my-project --service my-api --json guara env set --from-file .env --project my-project --service my-api --json guara env set NPM_TOKEN=xxx --build --project my-project --service my-api --json ``` The `--build` flag sets `availableAtBuild: true` on all variables provided in that invocation. Variables set without `--build` default to runtime-only. JSON response: `{ "data": { "set": ["DATABASE_URL", "NODE_ENV"], "total": 5 } }` Quiet response: `ok` ##### `guara env unset` Remove environment variables. Triggers a rolling restart. Requires project + service context. Accepts variadic key name arguments. ```bash guara env unset DATABASE_URL REDIS_URL --project my-project --service my-api --json ``` JSON response: `{ "data": { "removed": ["DATABASE_URL", "REDIS_URL"] } }` Quiet response: `ok` --- #### Domain Commands ##### `guara domains list` List domains for a service. Requires project + service context. ```bash guara domains list --project my-project --service my-api --json ``` JSON response: `{ "data": [DomainResponse, ...] }` Quiet response: one domain name per line ##### `guara domains add` Add a custom domain to a service. Requires project + service context. | Flag | Description | |------|-------------| | `--domain ` | Domain name to add (required) | ```bash guara domains add --domain api.myapp.com --project my-project --service my-api --json ``` JSON response: `{ "data": DomainResponse }` (includes `cname_target` for DNS setup) Quiet response: the domain ID ##### `guara domains remove` Remove a custom domain from a service. Requires project + service context. Requires `--yes` in non-interactive mode. | Flag | Description | |------|-------------| | `--domain ` | Domain name to remove (required) | ```bash guara domains remove --domain api.myapp.com --project my-project --service my-api --yes --json ``` JSON response: `{ "data": { "project": "my-project", "service": "my-api", "domain": "api.myapp.com", "action": "remove" } }` Quiet response: the domain name --- #### Scaling Commands ##### `guara scale` Scale service CPU or memory resources. Requires project + service context. At least one of `--cpu` or `--memory` is required. | Flag | Description | |------|-------------| | `--cpu ` | CPU in vCPUs (e.g., `0.25`, `0.5`, `1`) | | `--memory ` | Memory in MB (e.g., `256`, `512`, `1024`) | ```bash guara scale --cpu 0.5 --memory 512 --project my-project --service my-api --json ``` JSON response: ```json { "data": { "project": "my-project", "service": "my-api", "cpu": "0.5", "memory": "512", "before": { "cpu_request": "250m", "cpu_limit": "250m", "memory_request": "256Mi", "memory_limit": "256Mi" }, "after": { "cpu_request": "500m", "cpu_limit": "500m", "memory_request": "512Mi", "memory_limit": "512Mi" } } } ``` Quiet response: `ok` --- #### Log Commands ##### `guara logs` View service logs. Requires project + service context. | Flag | Short | Description | |------|-------|-------------| | `--follow` | `-f` | Follow logs in real time (polls every 2.5s) | | `--lines ` | `-n` | Number of log lines to fetch (default: `100`) | | `--since ` | | Start time — relative like `1h`, `30m`, `2d`, `90s` or ISO8601 | | `--until ` | | End time (ISO8601) | | `--search ` | | Filter logs by search text | | `--level ` | | Filter by log level: `trace`, `debug`, `info`, `warn`, `error`, `fatal` | ```bash guara logs --since 1h --level error --project my-project --service my-api --json ``` JSON response: `{ "data": [{ "timestamp": "...", "line": "...", "labels": { "level": "error" } }, ...] }` In `--follow --json` mode, each log entry is written as a separate JSON line (NDJSON). --- #### Link Commands ##### `guara link` Link the current directory to a GuaraCloud project (and optionally a service). Creates a `.guara.json` file. | Flag | Short | Description | |------|-------|-------------| | `--project ` | `-p` | Project slug (skips interactive picker) | | `--service ` | `-s` | Service slug (skips interactive picker) | ```bash guara link --project my-project --service my-api --json ``` JSON response: `{ "data": { "project": "my-project", "service": "my-api", "path": "/path/to/.guara.json" } }` Quiet response: `my-project/my-api` ##### `guara unlink` Remove the `.guara.json` link file from the current directory. ```bash guara unlink --json ``` JSON response: `{ "data": { "unlinked": true, "project": "my-project", "service": "my-api" } }` Quiet response: `unlinked` or `not_linked` --- #### Utility Commands ##### `guara status` Show GuaraCloud platform status. No authentication required. | Flag | Description | |------|-------------| | `--json` | Output as JSON | | `--api-url ` | Override the API base URL (also reads `GUARA_API_URL`) | ```bash guara status --json ``` JSON response: `{ "data": PlatformStatusResponse }` with `overall_status`, `components[]`, and `active_incidents[]`. ##### `guara open` Open project or service in the web dashboard. | Flag | Description | |------|-------------| | `--url` | Open the service live URL instead of the dashboard | ```bash guara open --project my-project --service my-api --json ``` JSON response: `{ "data": { "url": "https://app.guaracloud.com/projects/my-project/services/my-api" } }` Quiet response: the URL --- #### Config Commands ##### `guara config set ` Set a CLI configuration value. ```bash guara config set apiUrl https://api.staging.guaracloud.com --json ``` JSON response: `{ "data": { "apiUrl": "https://api.staging.guaracloud.com" } }` ##### `guara config get ` Get a CLI configuration value. ```bash guara config get apiUrl --json ``` JSON response: `{ "data": { "apiUrl": "https://api.staging.guaracloud.com" } }` ##### `guara config list` List all CLI configuration values. ```bash guara config list --json ``` JSON response: `{ "data": { "apiUrl": "...", ... } }` ##### `guara config reset` Reset CLI configuration to defaults. ```bash guara config reset --json ``` JSON response: `{ "data": { "message": "Configuration reset to defaults." } }` --- #### Session Commands (Proxy & Exec) ##### `guara proxy` Forward a local port to a private service running in your project's Kubernetes namespace. The local port is mapped to the service's container port via a secure WebSocket tunnel through the API. Useful for accessing databases, internal APIs, or admin panels that are not publicly exposed. Requires project + service context. The authenticated user must be a member of the project. | Flag | Short | Description | |------|-------|-------------| | `--local-port ` | | Local port to listen on (default: random available port) | ```bash # Forward local port 5432 to a Postgres service guara proxy --project my-project --service my-db --local-port 5432 # Forward using a random available local port guara proxy --project my-project --service my-db ``` Security notes: - Project membership is required; the API validates the caller's role before establishing the tunnel. - Sessions have idle timeouts — the connection is closed automatically after a period of inactivity. - All traffic is encrypted via WSS (WebSocket over TLS) between CLI and API. - The proxy binds to `127.0.0.1` only; it is not accessible from other machines on the network. ##### `guara exec` Open an interactive shell or execute a one-shot command inside a running service container. The session is proxied through a secure WebSocket connection to the Kubernetes pod. Requires project + service context. The authenticated user must be a member of the project. | Flag | Short | Description | |------|-------|-------------| | `--shell ` | | Shell to use for interactive mode (default: `/bin/sh`) | Two modes: 1. **Interactive mode** (no `--` separator): Opens an interactive terminal session in the container. 2. **One-shot mode** (with `--` separator): Executes the command after `--` and returns the output. ```bash # Interactive shell (opens /bin/sh by default) guara exec --project my-project --service my-api # Interactive shell with bash guara exec --project my-project --service my-api --shell /bin/bash # One-shot command execution guara exec --project my-project --service my-api -- ls -la /app # One-shot: run a database migration guara exec --project my-project --service my-api -- npx prisma migrate deploy ``` Security notes: - Project membership is required; the API validates the caller's role before establishing the session. - Sessions have idle timeouts — the connection is closed automatically after a period of inactivity. - All traffic is encrypted via WSS (WebSocket over TLS) between CLI and API. - One-shot commands return the exit code of the executed command. --- ### Workflow Recipes #### Deploy a new app from scratch ```bash # 1. Create a project PROJECT=$(guara projects create --name my-saas --quiet) # 2. Create a service guara services create \ --name api \ --build-method dockerfile \ --port 3000 \ --repo https://github.com/user/repo \ --project "$PROJECT" \ --json # 3. Set environment variables guara env set DATABASE_URL=postgres://host/db SECRET_KEY=abc123 \ --project "$PROJECT" --service api # 3b. Set a build-time variable (e.g., private registry token) guara env set NPM_TOKEN=xxx --build --project "$PROJECT" --service api # 4. Trigger deployment (polls until complete) guara deploy --branch main --project "$PROJECT" --service api --json ``` #### Deploy an existing service ```bash # Link directory to project/service (one-time setup) guara link --project my-saas --service api # Deploy (uses .guara.json context) guara deploy --json ``` #### Set environment variables from a file ```bash guara env set --from-file .env.production --project my-saas --service api ``` #### Rollback to a previous deployment ```bash # Find a healthy deployment to rollback to DEPLOY_ID=$(guara deployments list --project my-saas --service api --json | jq -r '.data[] | select(.status == "healthy") | .id' | head -1) # Rollback guara rollback --deployment "$DEPLOY_ID" --project my-saas --service api --json ``` #### Scale resources ```bash guara scale --cpu 1 --memory 1024 --project my-saas --service api --json ``` #### Add a custom domain ```bash # Add domain (returns CNAME target) guara domains add --domain app.example.com --project my-saas --service api --json # After configuring DNS, verify via the API: # POST /v1/projects/:projectId/services/:serviceId/domains/:id/verify ``` #### CI/CD integration ```bash export GUARA_API_KEY=gk_live_xxxxx export GUARA_PROJECT=my-saas export GUARA_SERVICE=api # Deploy and capture result RESULT=$(guara deploy --branch "$CI_BRANCH" --json) STATUS=$(echo "$RESULT" | jq -r '.data.status') if [ "$STATUS" != "healthy" ]; then echo "Deployment failed: $(echo "$RESULT" | jq -r '.data.error_message')" exit 1 fi echo "Live at $(echo "$RESULT" | jq -r '.data.url')" ``` --- ### Error Code Reference These error codes appear in `--json` error output under `error.code`: | Error Code | HTTP Status | Meaning | |------------|-------------|---------| | CONCURRENT_BUILD | 409 | A build is already in progress | | TIER_LIMIT_EXCEEDED | 403 | Resource exceeds tier quota | | BUILD_QUOTA_EXCEEDED | 403 | Build minutes quota exceeded | | RESOURCE_POOL_EXCEEDED | 403 | Account resource pool exhausted | | PROJECT_LIMIT_EXCEEDED | 403 | Max projects reached for tier | | SERVICE_LIMIT_EXCEEDED | 403 | Max services reached for project | | DOMAIN_LIMIT_EXCEEDED | 403 | Max custom domains reached | | API_KEY_LIMIT_EXCEEDED | 403 | Max API keys reached for tier | | INVALID_API_KEY | 401 | API key is invalid or revoked | | API_KEY_EXPIRED | 401 | API key has expired | | API_KEY_SCOPE_INSUFFICIENT | 403 | API key lacks required permission scope | | ACCOUNT_SUSPENDED | 403 | Account suspended due to billing | | RATE_LIMITED | 429 | Too many requests | | PROJECT_NOT_FOUND | 404 | Project not found or no access | | SERVICE_NOT_FOUND | 404 | Service not found or no access | | DEPLOYMENT_NOT_FOUND | 404 | Deployment not found | | PAYMENT_FAILED | 402 | Payment failed | --- ### Notes for AI Agents - Always use `--json` for structured, parseable output. Use `--quiet` when you only need an ID or slug. - Always pass `--yes` (`-y`) for destructive operations (`services delete`, `domains remove`) to skip interactive confirmation. - Set `GUARA_API_KEY` and `GUARA_PROJECT`/`GUARA_SERVICE` as environment variables to avoid passing flags on every command. - The `guara deploy` command polls until the deployment is terminal. In `--quiet` mode it returns the deployment ID immediately without polling. - The `guara env set` command accepts variadic `KEY=value` arguments (no flag prefix needed) and also supports `--from-file` for `.env` files. Use `--build` (`-b`) to mark variables as available at build time (Docker build args). - The `guara env unset` command accepts variadic key names as bare arguments. - The `guara scale` command accepts CPU as vCPUs (float like `0.25`, `0.5`, `1`) and memory as MB (integer like `256`, `512`, `1024`). It converts these to Kubernetes units internally. - All commands that require project/service context will fail with exit code 1 and a helpful suggestion if context cannot be resolved. - `guara status` does not require authentication and can be used to check platform health. - Browser login (`guara login --browser`) starts a local HTTP server on a random port and opens the browser. It times out after 120 seconds. Not suitable for headless CI/CD — use `--api-key` instead. - Config is stored at `~/.config/guara/config.json`. The link file is `.guara.json` in the project directory. - The `guara proxy` command opens a long-lived local TCP listener. It blocks until the user terminates it. Supports `--json` and `--quiet` for outputting connection info at startup. - The `guara exec` command in interactive mode requires a TTY. In one-shot mode (with `--`), it runs the command and exits. Use one-shot mode for automated/scripted workflows. - Both `guara proxy` and `guara exec` require project membership and establish WebSocket tunnels with idle timeouts. Sessions are automatically cleaned up on disconnect.