eve-manifest-authoring

v2026.09.25

Author and maintain Eve manifest files (.eve/manifest.yaml) for services, environments, pipelines, workflows, and secret interpolation. Use when changing deployment shape or runtime configuration in an Eve-compatible repo.

GitHub
Install command
npx skhub add incept5/eve-manifest-authoring
Markdown
SKILL.md

Eve Manifest Authoring

Keep the manifest as the single source of truth for build and deploy behavior.

Minimal skeleton (v2)

schema: eve/compose/v2
project: my-project

registry: "eve"  # Use managed registry by default for Eve apps
services:
  api:
    build:
      context: ./apps/api           # Build context directory
      dockerfile: Dockerfile        # Optional, defaults to context/Dockerfile
    # image omitted by default; when build is present, Eve derives image name from service key
    ports: [3000]
    environment:
      NODE_ENV: production
    x-eve:
      ingress:
        public: true
        port: 3000

environments:
  staging:
    pipeline: deploy
    pipeline_inputs:
      some_key: default_value

pipelines:
  deploy:
    steps:
      - name: build
        action:
          type: build               # Builds all services with build: config
      - name: release
        depends_on: [build]
        action:
          type: release
      - name: deploy
        depends_on: [release]
        action:
          type: deploy

Registry Image Labels

Some registries require package metadata for permission and ownership inheritance. Add these labels to your Dockerfiles when supported by your registry:

LABEL org.opencontainers.image.source="https://github.com/YOUR_ORG/YOUR_REPO"
LABEL org.opencontainers.image.description="Service description"

Why this matters: Metadata helps preserve repository ownership and improves traceability. The Eve builder injects these labels automatically, but including them in your Dockerfile is still recommended.

For multi-stage Dockerfiles, add the labels to the final stage (the production image).

Registry Modes

registry: "eve"     # Eve-native registry (internal JWT auth)
registry: "none"    # Disable registry handling (public images)
registry:           # BYO registry (full object — see section below)
  host: public.ecr.aws/w7c4v0w3
  namespace: myorg
  auth: { username_secret: REGISTRY_USERNAME, token_secret: REGISTRY_PASSWORD }

For BYO/private registries, provide:

registry:
  host: public.ecr.aws/w7c4v0w3
  namespace: myorg
  auth:
    username_secret: REGISTRY_USERNAME
    token_secret: REGISTRY_PASSWORD

Managed Databases

Declare platform-provisioned databases with x-eve.role: managed_db:

services:
  db:
    x-eve:
      role: managed_db
      managed:
        class: db.p1
        engine: postgres
        engine_version: "16"

Not deployed to K8s — provisioned by the orchestrator on first deploy. Reference managed values elsewhere: ${managed.db.url}.

Eve-Migrate for Database Migrations

Use the platform's migration runner instead of Flyway, TypeORM, or Knex. It uses plain SQL files with timestamp prefixes, tracked in schema_migrations:

services:
  migrate:
    image: public.ecr.aws/w7c4v0w3/eve-horizon/migrate:latest
    environment:
      DATABASE_URL: ${managed.db.url}
      MIGRATIONS_DIR: /migrations
    x-eve:
      role: job
      files:
        - source: db/migrations
          target: /migrations

Migration files: db/migrations/20260312000000_initial_schema.sql. The x-eve.files directive mounts them into the container at /migrations.

In the pipeline, the migrate step must run after deploy (managed DB needs provisioning):

pipelines:
  deploy:
    steps:
      - name: build
        action: { type: build }
      - name: release
        depends_on: [build]
        action: { type: release }
      - name: deploy
        depends_on: [release]
        action: { type: deploy }
      - name: migrate
        depends_on: [deploy]
        action: { type: job, service: migrate }

For local dev, use the same image via Docker Compose for parity:

# docker-compose.yml
services:
  migrate:
    image: ghcr.io/eve-horizon/eve-migrate:latest
    environment:
      DATABASE_URL: postgres://app:app@db:5432/myapp
    volumes:
      - ./db/migrations:/migrations:ro
    depends_on:
      db: { condition: service_healthy }

Legacy manifests

If the repo still uses components: from older manifests, migrate to services: and add schema: eve/compose/v2. Keep ports and env keys the same.

Services

  • Provide image and optionally build (context and dockerfile).
  • Use ports, environment, healthcheck, depends_on as needed.
  • Use x-eve.external: true and x-eve.connection_url for externally hosted services.
  • Use x-eve.role: job for one-off services (migrations, seeds). For database migrations, prefer Eve's eve-migrate image (see below).

Build configuration

Services with Docker images should define their build configuration:

services:
  api:
    build:
      context: ./apps/api           # Build context directory
      dockerfile: Dockerfile        # Optional, defaults to context/Dockerfile
    # image: api      # optional if using build; managed registry derives this
    ports: [3000]

Note: Every deploy pipeline should include a build step before release. The build step creates tracked BuildSpec/BuildRun records and produces image digests that releases use for deterministic deployments.

Local dev alignment

  • Keep service names and ports aligned with Docker Compose.
  • Prefer ${secret.KEY} and use .eve/dev-secrets.yaml for local values.

Environments, pipelines, workflows

  • Link each environment to a pipeline via environments.<env>.pipeline.
  • When pipeline is set, eve env deploy <env> triggers that pipeline instead of direct deploy.
  • Use environments.<env>.pipeline_inputs to provide default inputs for pipeline runs.
  • Override inputs at runtime with eve env deploy <env> --ref <sha> --inputs '{"key":"value"}' --repo-dir ./my-app.
  • Use --direct flag to bypass pipeline and do direct deploy: eve env deploy <env> --ref <sha> --direct --repo-dir ./my-app.
  • Pipeline steps can be action, script, or agent.
  • Use action.type: create-pr for PR automation when configured.
  • Workflows live under workflows and are invoked via CLI; db_access is honored.
  • Workflow steps support harness, harness_profile, and harness_options per step (overrides agent-resolved values), condition for skip-if-false gating against upstream step status, and git controls (now correctly materialized at dispatch). See references/manifest.md for the full step shape.
  • Workflow- and step-level scope blocks narrow the step job token and org filesystem mount. Supported axes: orgfs, orgdocs, envdb, cloud_fs. Step scope intersects workflow scope — invocation scope may narrow but not widen. Use it to grant a step exactly the paths/tables/mounts it needs, default-deny everything else.
workflows:
  create-design:
    scope:
      orgfs: { allow_prefixes: [/groups/projects/proj-a/**] }
    steps:
      - name: publish
        scope:
          cloud_fs: { allow_mount_ids: [mount_a] }
        agent: { name: publisher }

Request-supplied scope (workflow invoke API body) requires jobs:harness_override. No --scope-* CLI flag yet.

Custom Domains and Stable Egress

Bring-your-own hostnames and opt-in network shape are declared per service:

services:
  api:
    x-eve:
      ingress:
        public: true
        port: 3000
        domains: ["app.example.com", "www.example.com"]    # max 10
      networking:
        egress: stable    # default 'nat'; opt in only when a vendor needs allowlisted source IPs
  • ingress.domains are bound on first deploy: the first env to deploy with a hostname owns it; other envs that reference the same hostname log owned by environment "<A>" and skip rendering. Operate ownership with eve domain list|verify|status|transfer|unbind|remove (see eve-deploy-debugging).
  • networking.egress: stable schedules the pod on the stable-egress node group with hostNetwork: true. It bypasses NAT and constrains scheduling — only opt in when needed.

See references/manifest.md for the full field shape (validation rules, max counts, per-env overrides).

Project Branding And Auth

App-facing login, invite emails, and magic-link emails are project-scoped and declared in x-eve.branding / x-eve.auth. eve project sync writes both onto the project record; the SSO broker and Eve API mailer read from there.

x-eve.branding — invite + magic-link email branding

x-eve:
  branding:
    app_name: "ACME Portal"                                    # required, <=60 chars
    app_logo_url: "https://app.example.com/logo.svg"          # https only on emitted mail
    primary_color: "#1f6feb"                                  # six-digit hex
    email_from_name: "ACME Portal"                              # From-name header
    reply_to_email: "support@example.com"
    support_email: "support@example.com"
    support_url: "https://example.com/help"

email_from_name, reply_to_email, support_email set headers on app-scoped mail while keeping the platform's verified sender address. Phase 1 emits logo + name + primary color in invite and magic-link templates.

x-eve.auth.login_method: magic_link — passwordless apps

x-eve:
  auth:
    login_method: magic_link            # password_or_magic_link | password | magic_link
    self_signup: false
    invite_requires_password: false

Opt in per project. With magic_link, the SSO login page hides password and signup, and Eve API sends branded magic-link mail via POST /auth/magic-link (not GoTrue direct), so unknown emails get generic success without creating a GoTrue user when self_signup: false. With invite_requires_password: false, invite acceptance establishes the session and skips /set-password. Use password_or_magic_link to keep password login plus a secondary branded magic-link control.

x-eve.auth.org_access.domain_signup — pre-approved email domains

x-eve:
  auth:
    org_access:
      mode: allowlist
      allowed_orgs: [org_acme, org_partner]
      domain_signup:
        enabled: true
        domains:
          - { domain: acme.com,   target_org: org_acme,  role: member }
          - { domain: partner.example,  target_org: org_partner }
          - { domain: "*.acme.com", target_org: org_acme }  # wildcard; apex needs its own rule

v2 shape (2026-05-12). Each domains[] entry is an object with its own required target_org; the legacy list-of-strings + block-level target_org is no longer accepted. Each target_org must already appear in allowed_orgs. Matching is first-rule-in-declaration-order; declare more-specific patterns first. Invalid with login_method: password. Free-email domains (free-mail.example, outlook.com, ...) emit a coherence warning — declaring them lets anyone on Earth join.

x-eve.auth.allowed_redirect_origins — custom-domain apps

x-eve:
  auth:
    allowed_redirect_origins:
      - https://app.example.com
      - https://www.example.com

Origins only — scheme://host[:port]. Paths, queries, fragments, and userinfo are rejected at sync. http:// permitted only for localhost, loopback IPs, and *.lvh.me. The SSO broker also auto-includes the project's eligible custom_domains rows and (in allowlist mode) custom domains owned by allowed_orgs siblings, so a project with a registered custom domain does not need to repeat it here. Confirm the resolved list with eve project auth-context <project_id>.

See references/manifest.md for full validation rules (email-domain grammar, IDN normalization, duplicate detection, redirect-origin normalization).

Service Token Permissions

Every deployed service receives an auto-injected EVE_SERVICE_TOKEN with read-only defaults. Declare any write scopes you need explicitly — anything not declared is denied:

services:
  api:
    x-eve:
      permissions: [jobs:write, events:write, threads:write]

Declared permissions are merged with the read-only defaults; you only list what you add. Default to declaring nothing and grant write scopes one at a time as the service actually needs them. See references/manifest.md for the full permission catalog.

Platform-Injected Environment Variables

Eve automatically injects these into all deployed service containers:

VariableDescription
EVE_API_URLInternal cluster URL for server-to-server calls
EVE_PUBLIC_API_URLPublic ingress URL for browser-facing apps
EVE_PROJECT_IDThe project ID
EVE_ORG_IDThe organization ID
EVE_ENV_NAMEThe environment name

Use EVE_API_URL for backend calls from your container. Use EVE_PUBLIC_API_URL for browser/client-side code. Services can override these in their environment section.

Interpolation and secrets

  • Env interpolation: ${ENV_NAME}, ${PROJECT_ID}, ${ORG_ID}, ${ORG_SLUG}, ${COMPONENT_NAME}.
  • Secret interpolation: ${secret.KEY} pulls from Eve secrets or .eve/dev-secrets.yaml.
  • Managed DB interpolation: ${managed.<service>.<field>} resolves at deploy time.
  • Use .eve/dev-secrets.yaml for local overrides; set real secrets via the API for production.

Eve extensions

  • Top-level defaults via x-eve.defaults (env, harness, harness_profile, harness_options, hints, git, workspace).
  • Top-level agent policy via x-eve.agents (profiles, councils, availability rules).
  • Agent packs via x-eve.packs with optional x-eve.install_agents defaults.
  • Agent config paths via x-eve.agents.config_path and x-eve.agents.teams_path.
  • Chat routing config via x-eve.chat.config_path.
  • Service extensions under x-eve (ingress, role, api specs, worker pools, cli, object_store, networking, permissions).
  • API specs: x-eve.api_spec or x-eve.api_specs (spec URL relative to service by default).
  • App CLI: x-eve.cli declares an agent-friendly CLI for the service (see below).
  • Toolchains: agent-level toolchains declarations inject on-demand runtimes (see below).
  • Cloud FS mounts: configured via integrations, not the manifest (see references/integrations.md).
  • Per-org OAuth: each org registers its own OAuth app credentials via eve integrations configure (see eve-auth-and-secrets).

Example:

x-eve:
  agents:
    version: 1
    config_path: agents/agents.yaml
    teams_path: agents/teams.yaml
  chat:
    config_path: agents/chat.yaml
  install_agents: [claude-code, codex]
  packs:
    - source: ./skillpacks/my-pack

App CLI Framework

Recommended for every app with an API. Wrap your service API in a CLI and register it here. Agents strongly prefer CLI commands over raw REST — CLIs handle auth, URL construction, and error formatting transparently. This is the Eve way: coding agents should build a CLI in the repo and register it in the manifest rather than having agents call APIs directly.

The platform auto-discovers services with x-eve.cli or x-eve.api_spec from the manifest and makes them available on $PATH for all agent jobs in the project — no explicit with_apis needed. Just declare the CLI here and all agents get it.

services:
  api:
    x-eve:
      api_spec:
        type: openapi
      cli:
        name: myapp              # Binary name (goes on $PATH)
        bin: cli/bin/myapp       # Path relative to repo root (pre-bundled)

For compiled CLIs, use an image-based distribution:

services:
  api:
    x-eve:
      cli:
        name: myapp
        image: ghcr.io/org/myapp-cli:latest    # Pre-built image

How It Works

  1. Manifest sync stores CLI metadata alongside api_spec.
  2. Job workspace setup (after clone) runs chmod +x and symlinks the binary to /usr/local/bin/.
  3. For image-based CLIs, an init container copies the binary from the image.
  4. The CLI reads EVE_APP_API_URL_{SERVICE} and EVE_JOB_TOKEN (already injected) -- no manual auth or URL configuration.

CLI naming rules

  • Lowercase alphanumeric with hyphens: [a-z][a-z0-9-]*
  • Must be unique per project
  • The name becomes the command agents invoke (e.g., eden projects list)

Worker Toolchain Declarations

Agents declare which runtime toolchains they need. The platform injects them as init containers -- no fat worker image required.

# In agents.yaml
agents:
  data-analyst:
    name: Data Analyst
    skill: analyze-data
    harness_profile: claude-sonnet
    toolchains: [python]           # Needs python + uv

  doc-processor:
    name: Document Processor
    skill: process-documents
    harness_profile: claude-sonnet
    toolchains: [media]            # Needs ffmpeg + whisper

Available toolchains: python, media, rust, java, kotlin.

Workflows can override agent defaults:

workflows:
  process-document:
    steps:
      - name: process
        agent: doc-processor
        toolchains: [media, python]  # Override: needs both

Toolchains are mounted at /opt/eve/toolchains/{name}/ with binaries on $PATH. The default worker image is base (~800MB); toolchains add only what each job needs.

Cloud FS Mounts

Google Drive folders can be mounted into the org filesystem via cloud FS integrations. Configuration is through the integrations system, not the manifest.

# Org admin registers Google Drive OAuth app credentials (BYOA)
eve integrations configure google-drive \
  --client-id "xxx.apps.googleusercontent.com" \
  --client-secret "GOCSPX-xxx"

# Connect and mount a Drive folder
eve integrations connect google-drive
eve cloud-fs mount --org org_xxx \
  --provider google-drive \
  --folder-id <drive-folder-id> \
  --label "Shared Drive"

Developers browse mounted content with eve cloud-fs ls and search it with eve cloud-fs search. The mount stores the provider folder ID plus an optional human-readable root-folder path hint; there is no separate CLI --mount-path setting.

App Object Store

Declare app-scoped object storage buckets in the manifest. Each bucket is provisioned per environment during deploy, tracked in eve env diagnose, and credentials are injected as environment variables.

services:
  api:
    x-eve:
      object_store:
        buckets:
          - name: uploads
            visibility: private
          - name: avatars
            visibility: public
            cors:
              origins: ["*"]
              methods: [GET, PUT, HEAD]
              max_age_seconds: 3600

Auto-Injected Storage Environment Variables

When object store buckets are provisioned, these env vars are injected into the service container:

VariableDescription
STORAGE_ENDPOINTS3-compatible endpoint URL
STORAGE_REGIONStorage region
STORAGE_ACCESS_KEY_IDApp-facing access key
STORAGE_SECRET_ACCESS_KEYApp-facing secret key
STORAGE_BUCKET_<NAME>Physical bucket name for each logical bucket
STORAGE_FORCE_PATH_STYLEtrue for MinIO local dev, omitted for AWS S3

On AWS staging, apps currently share one app-bucket IAM principal scoped to demo-eve-app-*. It cannot access platform internal buckets or org filesystem buckets, but it does not isolate app buckets from each other. Per-app IRSA is the production follow-up.

Design Rules

  • One bucket per concern. Separate uploads from avatars from exports.
  • Set visibility intentionally. Only buckets serving public assets should be visibility: public.
  • Use CORS for browser uploads. Set cors.origins and cors.methods when the frontend uploads directly via presigned URLs.
  • Bucket names must be unique within a service. The platform derives the physical bucket name from the project, environment, and logical name.

For detailed storage layer documentation, see the eve-read-eve-docs skill: references/object-store-filesystem.md.

Discovery
Tags

No tags published for this skill.

Version
Latest version metadata

Version

v2026.09.25

Published

Sep 25, 2026

Category

Uncategorized

License

MIT

Source path

eve-se/eve-manifest-authoring

Default branch

main

Latest commit

d8b938d

Tree SHA

b4bd88b