Project Descriptor
A project descriptor is the compiled representation of your foundry.config.ts. The create-foundry-app CLI compiles your TypeScript config into this JSON shape and submits it to POST /projects/deploy. You can also construct and submit the descriptor directly via the API.
Top-level fields
| Field | Type | Required | Description |
|---|---|---|---|
name | string | yes | Stable project identity. Changing the name creates a new project rather than updating the existing one. |
resources | array | yes | Every declared resource. Exactly one must have kind: app. |
dependencies | object | no | Maps a resource logical_name to a list of logical_name values that must be Running before it is created. |
organization_id | UUID | no | Scopes the project to an organization you belong to. Omit for a personal project. |
Resources
Each entry in the resources array has three fields:
| Field | Type | Required | Description |
|---|---|---|---|
logical_name | string | yes | Unique name for this resource within the project. Used as the key in dependencies and as $name references inside other resources' specs. |
kind | string | yes | One of postgres, files, auth, app. |
spec | object | yes | Kind-specific configuration. Unknown keys are rejected. |
Dependency ordering
The dependencies map expresses which resources must reach Running before a given resource is created. Independent resources provision concurrently.
{
"dependencies": {
"auth": ["db"],
"web": ["db", "auth"]
}
}
The engine also discovers implicit ordering edges from $logical_name references inside spec values: if the web spec contains "$db.service_id", the engine treats db as a dependency of web even if you did not list it explicitly.
postgres spec
Provisions a managed PostgreSQL service.
| Field | Type | Default | Description |
|---|---|---|---|
plan | string | tier-1 | Compute plan name (e.g. tier-1, tier-2). |
storage_gb | integer | 25 | Data disk size in GB. |
storage_tier | string | platform default | Storage tier: standard or maxiops. |
version | string | platform default | PostgreSQL major version (e.g. "17"). |
extensions | string array | none | Extensions to install (e.g. ["pgvector", "postgis"]). |
default_database | string | defaultdb | Name of the default database created during provisioning. |
files spec
Provisions an S3-compatible object storage bucket.
| Field | Type | Default | Description |
|---|---|---|---|
quota_gb | integer | none | Soft quota in GB (notification threshold). |
quota_hard_gb | integer | none | Hard quota in GB (enforced ceiling). |
auth spec
Attaches end-user authentication to the app resource. Auth does not create a standalone service; it enables an auth_configuration on the app it depends on. The auth resource must declare a dependency on the app resource (or the engine infers it from $ref notation).
| Field | Type | Default | Description |
|---|---|---|---|
providers | string array | ["password"] | Enabled auth providers. Supported: password, google, github. |
smtp | object | none | SMTP configuration for magic-link email delivery. If absent, email-based flows are disabled. |
idp_providers | array | none | Social login provider configurations (client ID, secret). |
When auth is enabled the reconciler records an issuer_url in the resource's outputs once the auth configuration is active.
app spec
Provisions a hosted application container.
| Field | Type | Required | Description |
|---|---|---|---|
image | string | yes | Container image reference (e.g. ghcr.io/myorg/myapp:latest). |
port | integer | yes | The container port the application listens on. |
env | object | no | Additional environment variables to inject alongside the platform-managed ones. |
plan | string | no | Compute plan. Defaults to tier-1. |
storage_gb | integer | no | Persistent storage for the app container. |
custom_domains | string array | no | Custom domains to attach to the app. |
Complete example
The following descriptor declares a to-do application backed by PostgreSQL with the pgvector extension, end-user auth with password and Google login, and an application container.
{
"descriptor": {
"name": "my-todo-app",
"resources": [
{
"logical_name": "db",
"kind": "postgres",
"spec": {
"plan": "tier-2",
"storage_gb": 50,
"storage_tier": "maxiops",
"version": "17",
"extensions": ["pgvector"]
}
},
{
"logical_name": "uploads",
"kind": "files",
"spec": {
"quota_gb": 100
}
},
{
"logical_name": "auth",
"kind": "auth",
"spec": {
"providers": ["password", "google"],
"smtp": {
"host": "smtp.example.com",
"port": 587,
"username": "noreply@example.com",
"password": "smtp-secret"
},
"idp_providers": [
{
"provider": "google",
"client_id": "your-google-client-id",
"client_secret": "your-google-client-secret"
}
]
}
},
{
"logical_name": "web",
"kind": "app",
"spec": {
"image": "ghcr.io/myorg/todo:latest",
"port": 3000,
"plan": "tier-2",
"env": {
"NODE_ENV": "production",
"LOG_LEVEL": "info"
}
}
}
],
"dependencies": {
"auth": ["db"],
"web": ["db", "uploads", "auth"]
}
}
}
When this project reaches Running, the web container will have the following environment variables injected by the platform, in addition to those declared in env:
| Variable | Source |
|---|---|
DATABASE_URL | PostgreSQL connection string for db |
S3_ENDPOINT | Files endpoint for uploads |
S3_BUCKET | Bucket name for uploads |
S3_ACCESS_KEY | Access key ID for uploads |
S3_SECRET | Secret access key for uploads |
FOUNDRY_API_TOKEN | Scoped platform token (read-only: services, files) |
FOUNDRY_API_URL | Base URL of the FoundryDB API |
Resource naming
The engine derives stable service names from the project name and the resource's logical_name. The same project-plus-logical-name always yields the same service name, which keeps re-deploys pointing at the same underlying service. Names are lowercased, sanitized to [a-z0-9-], and capped at 60 characters.
Next steps
- Deploying a project — how
POST /projects/deployhandles the descriptor and what to poll.