@vertile-ai/iac 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +255 -0
- package/docs/README.md +40 -0
- package/docs/index.html +276 -0
- package/docs/manifest.md +130 -0
- package/docs/positioning.md +65 -0
- package/docs/roadmap.md +77 -0
- package/examples/dynomic/README.md +22 -0
- package/examples/dynomic/apps/admin/package.json +7 -0
- package/examples/dynomic/apps/admin/vercel.json +4 -0
- package/examples/dynomic/apps/web/package.json +7 -0
- package/examples/dynomic/apps/web/vercel.json +4 -0
- package/examples/dynomic/infrastructure/iac/iac.json +191 -0
- package/examples/dynomic/package.json +17 -0
- package/package.json +51 -0
- package/schema/iac.schema.json +266 -0
- package/src/apply.mjs +38 -0
- package/src/cli.mjs +74 -0
- package/src/core/args.mjs +36 -0
- package/src/core/concepts.mjs +26 -0
- package/src/core/context.mjs +39 -0
- package/src/core/hcl.mjs +110 -0
- package/src/core/manifest.mjs +110 -0
- package/src/core/render.mjs +38 -0
- package/src/core/terraform.mjs +42 -0
- package/src/core/vercel-manifests.mjs +179 -0
- package/src/plan.mjs +32 -0
- package/src/providers/aws/index.mjs +153 -0
- package/src/providers/digitalocean/index.mjs +85 -0
- package/src/providers/vercel/index.mjs +60 -0
- package/src/provision-env.mjs +798 -0
- package/src/reconcile-project-domains.mjs +549 -0
- package/src/reconcile-project-settings.mjs +385 -0
- package/src/render.mjs +27 -0
- package/src/shared.mjs +116 -0
- package/src/sync-env.mjs +297 -0
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# Positioning
|
|
2
|
+
|
|
3
|
+
Vertile AI IaC is for developers who want portable infrastructure intent without
|
|
4
|
+
operating a platform control plane.
|
|
5
|
+
|
|
6
|
+
## Why This Exists
|
|
7
|
+
|
|
8
|
+
In the AI era, more products are built by one-person companies and very small
|
|
9
|
+
teams. Collaboration overhead matters less than abstraction quality. A single
|
|
10
|
+
developer, or an AI working with that developer, should not need to hand-author
|
|
11
|
+
different infrastructure definitions for every provider.
|
|
12
|
+
|
|
13
|
+
Vertile AI IaC provides one manifest for app infrastructure needs:
|
|
14
|
+
|
|
15
|
+
- app hosting
|
|
16
|
+
- domains
|
|
17
|
+
- environment variables and secrets
|
|
18
|
+
- object storage
|
|
19
|
+
- databases
|
|
20
|
+
- queues
|
|
21
|
+
- sandboxes and runtimes
|
|
22
|
+
- clusters and compute
|
|
23
|
+
|
|
24
|
+
Provider adapters compile those needs into Vercel, AWS, DigitalOcean, and later
|
|
25
|
+
other providers.
|
|
26
|
+
|
|
27
|
+
## Crossplane Comparison
|
|
28
|
+
|
|
29
|
+
Crossplane is a strong reference point, but it makes Kubernetes the control
|
|
30
|
+
plane. Your app does not have to run on Kubernetes to use Crossplane, but the
|
|
31
|
+
Crossplane controllers do.
|
|
32
|
+
|
|
33
|
+
That is powerful for platform teams that need:
|
|
34
|
+
|
|
35
|
+
- RBAC
|
|
36
|
+
- audit logs
|
|
37
|
+
- CRDs
|
|
38
|
+
- admission policies
|
|
39
|
+
- namespaces
|
|
40
|
+
- Kubernetes secrets
|
|
41
|
+
- continuous controllers
|
|
42
|
+
|
|
43
|
+
Those are mostly developer-platform collaboration features. They are valuable
|
|
44
|
+
inside larger engineering organizations, but heavy for solo builders and small
|
|
45
|
+
teams that only want app infrastructure.
|
|
46
|
+
|
|
47
|
+
Vertile AI IaC takes a different position:
|
|
48
|
+
|
|
49
|
+
```text
|
|
50
|
+
Crossplane:
|
|
51
|
+
Kubernetes as the infrastructure control plane.
|
|
52
|
+
|
|
53
|
+
Vertile AI IaC:
|
|
54
|
+
Git repo + CLI as the infrastructure control surface.
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Terraform And OpenTofu
|
|
58
|
+
|
|
59
|
+
Terraform and OpenTofu are execution engines, not portable abstractions.
|
|
60
|
+
Terraform can manage many providers, but provider resources are not portable by
|
|
61
|
+
themselves. An AWS S3 bucket resource is not the same as a DigitalOcean Spaces
|
|
62
|
+
resource or a Cloudflare R2 resource.
|
|
63
|
+
|
|
64
|
+
Vertile AI IaC treats Terraform/OpenTofu files as generated output. The portable
|
|
65
|
+
source of truth is the manifest.
|
package/docs/roadmap.md
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# Roadmap
|
|
2
|
+
|
|
3
|
+
## Phase 1: Stable Compiler Core
|
|
4
|
+
|
|
5
|
+
- Keep `infrastructure/iac/iac.json` as the only user-authored source of truth.
|
|
6
|
+
- Render provider-specific Terraform into `.vertile/terraform/<provider>/`.
|
|
7
|
+
- Keep legacy Vercel API reconciliation working until the Terraform path is
|
|
8
|
+
verified.
|
|
9
|
+
- Support `render`, `plan`, and guarded `apply`.
|
|
10
|
+
- Add schema validation with clear errors.
|
|
11
|
+
|
|
12
|
+
## Phase 2: First-Class Resource Concepts
|
|
13
|
+
|
|
14
|
+
Move provider-specific escape hatches behind portable app infrastructure
|
|
15
|
+
concepts:
|
|
16
|
+
|
|
17
|
+
- `apps`
|
|
18
|
+
- `domains`
|
|
19
|
+
- `env`
|
|
20
|
+
- `objectStorage`
|
|
21
|
+
- `databases`
|
|
22
|
+
- `queues`
|
|
23
|
+
- `sandboxes`
|
|
24
|
+
- `clusters`
|
|
25
|
+
|
|
26
|
+
Each concept should have shared fields and optional provider overrides.
|
|
27
|
+
|
|
28
|
+
Current implementation status:
|
|
29
|
+
|
|
30
|
+
- `apps` and `domains` render to Vercel resources.
|
|
31
|
+
- `objectStorage` renders to AWS S3 and DigitalOcean Spaces.
|
|
32
|
+
- `databases` renders to AWS RDS and DigitalOcean Managed Databases.
|
|
33
|
+
- `queues` renders to AWS SQS.
|
|
34
|
+
- `sandboxes` and `clusters` render to AWS EC2 instances and DigitalOcean
|
|
35
|
+
Droplets.
|
|
36
|
+
|
|
37
|
+
## Phase 3: Provider Matrix
|
|
38
|
+
|
|
39
|
+
Initial providers:
|
|
40
|
+
|
|
41
|
+
- Vercel: apps, domains, env vars, project settings.
|
|
42
|
+
- AWS: S3, RDS or DynamoDB, SQS, Lambda or ECS, EC2 where needed.
|
|
43
|
+
- DigitalOcean: Spaces, Managed Databases, App Platform, Droplets, Kubernetes
|
|
44
|
+
where needed.
|
|
45
|
+
|
|
46
|
+
Likely later providers:
|
|
47
|
+
|
|
48
|
+
- Cloudflare for DNS, Workers, R2, and queues.
|
|
49
|
+
- Neon and Supabase for databases.
|
|
50
|
+
- Fly.io, Render, and Railway for app hosting.
|
|
51
|
+
- Modal, E2B, and Daytona for sandbox or runtime providers.
|
|
52
|
+
- Hetzner and Vultr for cheaper compute and clusters.
|
|
53
|
+
|
|
54
|
+
## Phase 4: AI-Native Workflow
|
|
55
|
+
|
|
56
|
+
- `vertile-iac explain`: explain a manifest in product language.
|
|
57
|
+
- `vertile-iac doctor`: detect unsupported mappings, missing credentials, and
|
|
58
|
+
risky settings.
|
|
59
|
+
- `vertile-iac migrate`: suggest moves between providers.
|
|
60
|
+
- Plan summaries that say what changes mean, not just what Terraform will do.
|
|
61
|
+
|
|
62
|
+
## Phase 5: State And Outputs
|
|
63
|
+
|
|
64
|
+
- Use local state by default.
|
|
65
|
+
- Support optional remote backend configuration later.
|
|
66
|
+
- Add drift detection through `plan`.
|
|
67
|
+
- Write provider outputs to `.vertile/outputs/<env>.json`.
|
|
68
|
+
|
|
69
|
+
## Guiding Rule
|
|
70
|
+
|
|
71
|
+
Vertile AI IaC should not become Terraform, Pulumi, or Crossplane.
|
|
72
|
+
|
|
73
|
+
It should stay focused on app-first portable infrastructure intent:
|
|
74
|
+
|
|
75
|
+
```text
|
|
76
|
+
one manifest -> provider adapters -> Terraform/OpenTofu execution
|
|
77
|
+
```
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Dynomic
|
|
2
|
+
|
|
3
|
+
Dynomic is a fixture project used to exercise Vertile AI IaC from the shape a
|
|
4
|
+
product repo would keep. It intentionally uses the full current manifest surface:
|
|
5
|
+
multiple Vercel apps, domains, preview/production env provisioning, local env
|
|
6
|
+
sync, portable storage/database/queue/compute concepts, provider overrides, and
|
|
7
|
+
provider-specific escape hatch resources.
|
|
8
|
+
|
|
9
|
+
It intentionally contains no real secrets. The env files use placeholder values
|
|
10
|
+
so package consumers can inspect and run dry-runs without extra setup.
|
|
11
|
+
|
|
12
|
+
## Commands
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
vertile-iac sync-env --repo-root examples/dynomic --variants=staging,production
|
|
16
|
+
vertile-iac env --repo-root examples/dynomic --targets=preview,production
|
|
17
|
+
vertile-iac projects --repo-root examples/dynomic --projects=web
|
|
18
|
+
vertile-iac domains --repo-root examples/dynomic --projects=web
|
|
19
|
+
vertile-iac render --repo-root examples/dynomic --target=all --env=production
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Apply commands require `VERCEL_TOKEN` and a real Vercel team/project.
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "../../../../schema/iac.schema.json",
|
|
3
|
+
"version": 1,
|
|
4
|
+
"project": {
|
|
5
|
+
"key": "dynomic",
|
|
6
|
+
"name": "dynomic"
|
|
7
|
+
},
|
|
8
|
+
"environments": ["development", "preview", "production"],
|
|
9
|
+
"providers": {
|
|
10
|
+
"vercel": {
|
|
11
|
+
"teamSlug": "vertile-ai",
|
|
12
|
+
"projectDefaults": {
|
|
13
|
+
"nodeVersion": "20.x",
|
|
14
|
+
"enableAffectedProjectsDeployments": true
|
|
15
|
+
},
|
|
16
|
+
"resources": [
|
|
17
|
+
{
|
|
18
|
+
"type": "vercel_project_environment_variable",
|
|
19
|
+
"name": "example_escape_hatch",
|
|
20
|
+
"values": {
|
|
21
|
+
"project_id": "prj_dynomic_placeholder",
|
|
22
|
+
"key": "ESCAPE_HATCH_EXAMPLE",
|
|
23
|
+
"value": "configured-through-provider-resource",
|
|
24
|
+
"target": ["preview"]
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
]
|
|
28
|
+
},
|
|
29
|
+
"aws": {
|
|
30
|
+
"region": "us-east-1",
|
|
31
|
+
"defaultTags": {
|
|
32
|
+
"Owner": "vertile-ai",
|
|
33
|
+
"Fixture": "dynomic"
|
|
34
|
+
},
|
|
35
|
+
"resources": [
|
|
36
|
+
{
|
|
37
|
+
"type": "aws_sns_topic",
|
|
38
|
+
"name": "events",
|
|
39
|
+
"values": {
|
|
40
|
+
"name": "dynomic-events"
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
]
|
|
44
|
+
},
|
|
45
|
+
"digitalocean": {
|
|
46
|
+
"region": "nyc3",
|
|
47
|
+
"version": ">= 2.0.0",
|
|
48
|
+
"resources": [
|
|
49
|
+
{
|
|
50
|
+
"type": "digitalocean_project",
|
|
51
|
+
"name": "main",
|
|
52
|
+
"values": {
|
|
53
|
+
"name": "dynomic"
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
]
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
"env": {
|
|
60
|
+
"sourceDir": "infrastructure",
|
|
61
|
+
"sync": {
|
|
62
|
+
"apps": ["web", "admin"],
|
|
63
|
+
"sharedKey": "shared"
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
"apps": [
|
|
67
|
+
{
|
|
68
|
+
"key": "web",
|
|
69
|
+
"id": "prj_dynomic_web_placeholder",
|
|
70
|
+
"name": "dynomic-web",
|
|
71
|
+
"framework": "nextjs",
|
|
72
|
+
"rootDirectory": "apps/web",
|
|
73
|
+
"nodeVersion": "20.x",
|
|
74
|
+
"domains": [
|
|
75
|
+
"dynomic.example.com",
|
|
76
|
+
{
|
|
77
|
+
"name": "preview.dynomic.example.com",
|
|
78
|
+
"gitBranch": "preview"
|
|
79
|
+
}
|
|
80
|
+
],
|
|
81
|
+
"providers": {
|
|
82
|
+
"vercel": {
|
|
83
|
+
"enableAffectedProjectsDeployments": true
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
"key": "admin",
|
|
89
|
+
"id": "prj_dynomic_admin_placeholder",
|
|
90
|
+
"name": "dynomic-admin",
|
|
91
|
+
"framework": "nextjs",
|
|
92
|
+
"rootDirectory": "apps/admin",
|
|
93
|
+
"nodeVersion": "20.x",
|
|
94
|
+
"domains": [
|
|
95
|
+
"admin.dynomic.example.com",
|
|
96
|
+
{
|
|
97
|
+
"name": "admin-preview.dynomic.example.com",
|
|
98
|
+
"gitBranch": "preview"
|
|
99
|
+
}
|
|
100
|
+
],
|
|
101
|
+
"env": {
|
|
102
|
+
"sharedPrefix": "ADMIN_"
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
],
|
|
106
|
+
"domains": [
|
|
107
|
+
{
|
|
108
|
+
"name": "www.dynomic.example.com",
|
|
109
|
+
"app": "web"
|
|
110
|
+
}
|
|
111
|
+
],
|
|
112
|
+
"objectStorage": [
|
|
113
|
+
{
|
|
114
|
+
"key": "uploads",
|
|
115
|
+
"visibility": "private",
|
|
116
|
+
"providers": {
|
|
117
|
+
"aws": {
|
|
118
|
+
"bucket": "dynomic-production-uploads",
|
|
119
|
+
"forceDestroy": true
|
|
120
|
+
},
|
|
121
|
+
"digitalocean": {
|
|
122
|
+
"name": "dynomic-uploads",
|
|
123
|
+
"acl": "private",
|
|
124
|
+
"region": "nyc3"
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
],
|
|
129
|
+
"databases": [
|
|
130
|
+
{
|
|
131
|
+
"key": "appdb",
|
|
132
|
+
"engine": "postgres",
|
|
133
|
+
"providers": {
|
|
134
|
+
"aws": {
|
|
135
|
+
"engineVersion": "16",
|
|
136
|
+
"instanceClass": "db.t4g.micro",
|
|
137
|
+
"allocatedStorage": 20,
|
|
138
|
+
"databaseName": "dynomic",
|
|
139
|
+
"username": "dynomic",
|
|
140
|
+
"skipFinalSnapshot": true
|
|
141
|
+
},
|
|
142
|
+
"digitalocean": {
|
|
143
|
+
"engine": "pg",
|
|
144
|
+
"version": "16",
|
|
145
|
+
"size": "db-s-1vcpu-1gb",
|
|
146
|
+
"nodeCount": 1
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
],
|
|
151
|
+
"queues": [
|
|
152
|
+
{
|
|
153
|
+
"key": "jobs",
|
|
154
|
+
"kind": "fifo",
|
|
155
|
+
"providers": {
|
|
156
|
+
"aws": {
|
|
157
|
+
"visibilityTimeoutSeconds": 60,
|
|
158
|
+
"messageRetentionSeconds": 345600
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
],
|
|
163
|
+
"sandboxes": [
|
|
164
|
+
{
|
|
165
|
+
"key": "runner",
|
|
166
|
+
"providers": {
|
|
167
|
+
"aws": {
|
|
168
|
+
"instanceType": "t3.micro"
|
|
169
|
+
},
|
|
170
|
+
"digitalocean": {
|
|
171
|
+
"sizeSlug": "s-1vcpu-1gb"
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
],
|
|
176
|
+
"clusters": [
|
|
177
|
+
{
|
|
178
|
+
"key": "workers",
|
|
179
|
+
"size": 2,
|
|
180
|
+
"providers": {
|
|
181
|
+
"aws": {
|
|
182
|
+
"instanceType": "t3.small"
|
|
183
|
+
},
|
|
184
|
+
"digitalocean": {
|
|
185
|
+
"nodes": 2,
|
|
186
|
+
"sizeSlug": "s-1vcpu-2gb"
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
]
|
|
191
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "dynomic",
|
|
3
|
+
"private": true,
|
|
4
|
+
"scripts": {
|
|
5
|
+
"iac:env:dry-run": "vertile-iac env --repo-root . --targets=preview,production",
|
|
6
|
+
"iac:env:apply": "vertile-iac env --repo-root . --targets=preview,production --apply",
|
|
7
|
+
"iac:projects:dry-run": "vertile-iac projects --repo-root .",
|
|
8
|
+
"iac:projects:apply": "vertile-iac projects --repo-root . --apply",
|
|
9
|
+
"iac:domains:dry-run": "vertile-iac domains --repo-root .",
|
|
10
|
+
"iac:domains:apply": "vertile-iac domains --repo-root . --apply",
|
|
11
|
+
"iac:sync-env": "vertile-iac sync-env --repo-root . --variants=staging,production",
|
|
12
|
+
"iac:render": "vertile-iac render --repo-root . --target=vercel --env=production"
|
|
13
|
+
},
|
|
14
|
+
"devDependencies": {
|
|
15
|
+
"@vertile-ai/iac": "0.0.1"
|
|
16
|
+
}
|
|
17
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@vertile-ai/iac",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "App-first portable infrastructure intent for Vercel, AWS, and DigitalOcean.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/vertile-ai/iac.git"
|
|
9
|
+
},
|
|
10
|
+
"bugs": {
|
|
11
|
+
"url": "https://github.com/vertile-ai/iac/issues"
|
|
12
|
+
},
|
|
13
|
+
"homepage": "https://github.com/vertile-ai/iac#readme",
|
|
14
|
+
"bin": {
|
|
15
|
+
"vertile-iac": "src/cli.mjs",
|
|
16
|
+
"vertile-iac-render": "src/render.mjs",
|
|
17
|
+
"vertile-iac-plan": "src/plan.mjs",
|
|
18
|
+
"vertile-iac-apply": "src/apply.mjs",
|
|
19
|
+
"vertile-iac-sync-env": "src/sync-env.mjs",
|
|
20
|
+
"vertile-iac-env": "src/provision-env.mjs",
|
|
21
|
+
"vertile-iac-projects": "src/reconcile-project-settings.mjs",
|
|
22
|
+
"vertile-iac-domains": "src/reconcile-project-domains.mjs"
|
|
23
|
+
},
|
|
24
|
+
"files": [
|
|
25
|
+
"src",
|
|
26
|
+
"docs",
|
|
27
|
+
"schema",
|
|
28
|
+
"examples",
|
|
29
|
+
"!examples/**/.env",
|
|
30
|
+
"!examples/**/.env.*",
|
|
31
|
+
"README.md"
|
|
32
|
+
],
|
|
33
|
+
"scripts": {
|
|
34
|
+
"test": "node --test",
|
|
35
|
+
"check": "find src -name '*.mjs' -print0 | xargs -0 -n1 node --check",
|
|
36
|
+
"sync-env": "node src/sync-env.mjs",
|
|
37
|
+
"env:dry-run": "node src/provision-env.mjs",
|
|
38
|
+
"env:apply": "node src/provision-env.mjs --apply",
|
|
39
|
+
"projects:dry-run": "node src/reconcile-project-settings.mjs",
|
|
40
|
+
"projects:apply": "node src/reconcile-project-settings.mjs --apply",
|
|
41
|
+
"domains:dry-run": "node src/reconcile-project-domains.mjs",
|
|
42
|
+
"domains:apply": "node src/reconcile-project-domains.mjs --apply"
|
|
43
|
+
},
|
|
44
|
+
"publishConfig": {
|
|
45
|
+
"access": "public"
|
|
46
|
+
},
|
|
47
|
+
"engines": {
|
|
48
|
+
"node": ">=20"
|
|
49
|
+
},
|
|
50
|
+
"packageManager": "pnpm@10.33.0"
|
|
51
|
+
}
|