Skip to main content

Manifest Authoring

The .eve/manifest.yaml file is the single source of truth for how your project builds, deploys, and runs. It uses Docker Compose-style service definitions extended with Eve-specific configuration under x-eve. This guide walks through every section of the manifest, from basic structure to advanced patterns.

Manifest purpose and location

Every Eve project has one manifest at .eve/manifest.yaml in the repository root. It declares:

  • Services — what containers your project runs
  • Environments — where those containers deploy (staging, production, PR previews)
  • Pipelines — how builds, tests, and deploys are orchestrated
  • Workflows — on-demand or scheduled operations
  • Platform extensions — agent profiles, skill packs, default job settings

When you run eve project sync, the CLI reads this file, validates it, and pushes the parsed configuration to the Eve API. The API stores a hash of the manifest so subsequent deploys can detect drift.

Top-level fields

A minimal manifest needs only services, but most projects define several top-level fields:

schema: eve/compose/v2
project: acme-web

registry:
host: public.ecr.aws/w7c4v0w3
namespace: eve-horizon
auth:
username_secret: REGISTRY_USERNAME
token_secret: REGISTRY_PASSWORD

services:
# ...

environments:
# ...

pipelines:
# ...

workflows:
# ...

x-eve:
# ...
FieldRequiredDescription
schemaNoSchema identifier, currently eve/compose/v2
projectNoHuman-friendly project slug (informational)
registryNoContainer registry config — "eve", "none", or an object
servicesYesCompose-style service definitions
environmentsNoDeploy targets with overrides
pipelinesNoDeterministic step-based pipelines
workflowsNoOn-demand workflow definitions
versioningNoRelease/version policy (semver, tagging)
x-eveNoEve-specific top-level extensions

Unknown fields are allowed for forward compatibility.

Registry

The registry block tells Eve where to push and pull container images:

# Full object form
registry:
host: public.ecr.aws/w7c4v0w3
namespace: eve-horizon
auth:
username_secret: REGISTRY_USERNAME
token_secret: REGISTRY_PASSWORD

Two shorthand string values are also accepted:

registry: "eve"    # Use the Eve-native internal registry (JWT-authenticated)
registry: "none" # Opt out of registry auth and pull secrets

When set to "eve", the deployer uses an API-issued JWT to authenticate with the internal registry. When set to "none", Eve assumes public images or external auth and skips creating Kubernetes imagePullSecrets.

Service definitions

Services follow Docker Compose conventions. Each key under services is a service name, and the value describes the container:

services:
api:
build:
context: ./apps/api
dockerfile: Dockerfile
image: ghcr.io/acme/api
ports: [3000]
environment:
NODE_ENV: production
DATABASE_URL: postgres://app:${secret.DB_PASSWORD}@db:5432/app
depends_on:
db:
condition: service_healthy
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
interval: 10s
timeout: 5s
retries: 3
start_period: 10s
x-eve:
ingress:
public: true
port: 3000

Standard service fields

FieldDescription
imageBase image (used for pull; also the push target when build is present)
buildBuild context with optional dockerfile and args
environmentEnvironment variable map (supports secret and variable interpolation)
portsContainer ports (Compose-style — numbers or "host:container" strings)
depends_onDependency ordering with optional health conditions
healthcheckDocker-style health check (test command, interval, timeout, retries, start_period)
x-eveEve extensions (see below)

Health checks

Health checks use the Docker Compose format. Eve uses them to determine when a service is ready:

healthcheck:
test: ["CMD", "pg_isready", "-U", "app"]
interval: 5s
timeout: 3s
retries: 5
start_period: 10s

The test field accepts either a string or an array. CMD is the standard test type.

Dependencies and ordering

Use depends_on with a condition to control startup ordering:

depends_on:
db:
condition: service_healthy

Supported conditions:

  • service_started (or started) — wait until the container starts
  • service_healthy (or healthy) — wait until the health check passes

Eve extensions (x-eve)

Every service can include an x-eve block (also accepted as x_eve) for Eve-specific configuration:

FieldDescription
rolecomponent (default), worker, job, or managed_db
ingressPublic routing: { public: true, port: 3000 }
api_specSingle API spec registration
api_specsMultiple API specs (array)
externalIf true, the service is not deployed (external dependency)
connection_urlConnection string for external services
worker_typeWorker pool type for worker-role services
filesMount repository files into the container
storagePersistent volume configuration
managedManaged DB config (requires role: managed_db)

Ingress

If a service exposes ports and your environment has a domain, Eve creates public ingress by default. You can explicitly control this:

x-eve:
ingress:
public: true
port: 3000
alias: my-api # Optional custom subdomain (3-63 chars, lowercase alphanumeric + hyphens)

Set public: false to disable ingress for a service that exposes ports.

API spec registration

Register an OpenAPI, PostgREST, or GraphQL spec so Eve can discover your service's API:

x-eve:
api_spec:
type: openapi # openapi | postgrest | graphql
spec_url: /openapi.json

The spec_url is relative to the service URL by default. When on_deploy is true (the default), Eve fetches the spec after each deployment.

File mounts

Mount files from your repository into the container:

x-eve:
files:
- source: ./config/app.conf # Relative path in repo
target: /etc/app/app.conf # Absolute path in container

Persistent storage

Attach a persistent volume to a service:

x-eve:
storage:
mount_path: /data
size: 10Gi
access_mode: ReadWriteOnce # ReadWriteOnce | ReadWriteMany | ReadOnlyMany
storage_class: standard

Secret interpolation

Reference secrets stored in Eve using the ${secret.KEY} syntax anywhere in environment values:

environment:
DATABASE_URL: postgres://app:${secret.DB_PASSWORD}@db:5432/app
API_KEY: ${secret.STRIPE_KEY}

Secrets are resolved at deploy time. The actual values never appear in the manifest or in the synced configuration.

You can declare required secrets for validation during sync:

x-eve:
requires:
secrets: [GITHUB_TOKEN, REGISTRY_TOKEN]

Pipeline steps can also declare their own secret requirements:

pipelines:
ci:
steps:
- name: integration-tests
script:
run: "pnpm test"
requires:
secrets: [DATABASE_URL]

Use eve manifest validate or eve project sync --validate-secrets to verify all required secrets are present before deploying.

Variable interpolation

Eve injects platform environment variables into all deployed services automatically:

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 (e.g., proj_01abc123)
EVE_ORG_IDThe organization ID (e.g., org_01xyz789)
EVE_ENV_NAMEThe environment name (e.g., staging)

You can reference these in your application code without declaring them in the manifest:

// Server-side: internal cluster networking
const eveApi = process.env.EVE_API_URL;

// Client-side: public ingress for browser calls
const publicApi = process.env.EVE_PUBLIC_API_URL;

Services can override these values by defining them explicitly in their environment block.

Managed databases

Declare a platform-managed database by setting x-eve.role: managed_db:

services:
db:
x-eve:
role: managed_db
managed:
class: db.p1 # Tier: db.p1, db.p2, db.p3
engine: postgres
engine_version: "16"

Managed database services are not deployed to Kubernetes. Instead, Eve provisions a database tenant when you deploy an environment for the first time. Key points:

  • Provisioning happens on first deploy for each environment
  • Credentials are managed by the platform and can be rotated with eve db commands
  • Scaling is handled with eve db commands
  • Status can be checked with eve db commands

Other services can reference managed database values using ${managed.<service>.<field>} placeholders, which are resolved at deploy time.

Default job settings (x-eve.defaults)

The top-level x-eve.defaults block sets project-wide defaults for job execution — harness selection, model preferences, git behavior, and resource budgets:

x-eve:
defaults:
env: staging
harness: mclaude
harness_profile: primary-orchestrator
harness_options:
model: opus-4.5
reasoning_effort: high
hints:
permission_policy: auto_edit
resource_class: job.c1
max_cost:
currency: usd
amount: 5
max_tokens: 200000
git:
ref_policy: auto
branch: job/${job_id}
create_branch: if_missing
commit: manual
push: never
workspace:
mode: job

These defaults apply to all jobs in the project unless overridden at the job level.

Agent profiles (x-eve.agents)

Define per-project agent profiles and councils for AI orchestration:

x-eve:
agents:
version: 1
availability:
drop_unavailable: true
profiles:
primary-orchestrator:
- harness: mclaude
model: opus-4.5
reasoning_effort: high
primary-reviewer:
- harness: mclaude
model: opus-4.5
reasoning_effort: high
- harness: codex
model: gpt-5.2-codex
reasoning_effort: x-high
planning-council:
- profile: primary-planner
- harness: gemini
model: gemini-3

Profiles list harness/model combinations in preference order. If drop_unavailable is true, unavailable harnesses are silently skipped. The manifest sync API returns the parsed policy as parsed_agents for orchestrators to consume.

AgentPacks (x-eve.packs)

AgentPacks import agent, team, chat, and skill configuration from external repositories:

x-eve:
# Which agents to install skills for (defaults to [claude-code])
install_agents: [claude-code, codex, gemini-cli]

packs:
# Local pack
- source: ./skillpacks/my-pack

# Remote pack (ref required for remote sources)
- source: incept5/eve-skillpacks
ref: 0123456789abcdef0123456789abcdef01234567

# Per-pack agent override
- source: ./skillpacks/claude-only
install_agents: [claude-code]
  • source can be a local path, owner/repo, github:owner/repo, or a git URL.
  • Remote sources require ref as a 40-character git SHA.
  • Packs are resolved by eve agents sync and locked in .eve/packs.lock.yaml.
  • Use eve packs status to check lockfile state and drift.

Validating your manifest

Before syncing, validate the manifest locally:

eve manifest validate

This runs schema validation and, optionally, secret validation:

eve manifest validate --validate-secrets
eve manifest validate --strict # Fail on missing secrets

The validator checks:

  • Schema conformance against the Zod-based manifest schema
  • Service coherence (e.g., build without image and no registry configured)
  • Pipeline references (environment pointing to a non-existent pipeline)
  • Secret requirements (when --validate-secrets is set)

Syncing to the platform

Push the manifest to Eve so the platform can act on it:

eve project sync

This reads .eve/manifest.yaml, hashes it, and sends it to the API. The response includes:

  • manifest_hash — used by subsequent deploys to detect drift
  • parsed_defaults — the resolved x-eve.defaults block
  • parsed_agents — the resolved x-eve.agents block
  • warnings — any coherence issues detected
  • secret_validation — results if --validate-secrets was passed

Use --strict to fail on any validation warnings:

eve project sync --validate-secrets --strict

Common patterns

Fullstack app with managed database

schema: eve/compose/v2
project: acme-web
registry: "eve"

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

api:
build:
context: ./apps/api
image: acme-api
ports: [3000]
environment:
DATABASE_URL: ${managed.db.url}
NODE_ENV: production
depends_on:
db:
condition: service_healthy
x-eve:
ingress:
public: true
port: 3000
api_spec:
type: openapi
spec_url: /openapi.json

web:
build:
context: ./apps/web
image: acme-web
ports: [80]
x-eve:
ingress:
public: true
port: 80

migrate:
image: flyway/flyway:10
command: -url=jdbc:postgresql://db:5432/app -user=app migrate
depends_on:
db:
condition: service_healthy
x-eve:
role: job

environments:
staging:
pipeline: deploy
production:
pipeline: deploy
approval: required

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

Migration-only service

services:
migrate:
image: flyway/flyway:10
command: >-
-url=jdbc:postgresql://db:5432/app
-user=app
-password=${secret.DB_PASSWORD}
-locations=filesystem:/migrations
migrate
volumes:
- ./db/migrations:/migrations:ro
depends_on:
db:
condition: service_healthy
x-eve:
role: job

Services with role: job are runnable as one-off tasks (migrations, seed scripts). They don't run as long-lived deployments.

External service reference

services:
stripe:
x-eve:
external: true
connection_url: https://api.stripe.com

External services are not deployed by Eve. They exist in the manifest so other services and the platform can reference them as dependencies.

What's next?