@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
package/README.md
ADDED
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
# @vertile-ai/iac
|
|
2
|
+
|
|
3
|
+
Opinionated infrastructure-as-code tooling.
|
|
4
|
+
|
|
5
|
+
Install once, define infrastructure once, then run `vertile-iac plan` or
|
|
6
|
+
guarded `vertile-iac apply` to manage Vercel, AWS, and DigitalOcean changes
|
|
7
|
+
from the same IaC source of truth.
|
|
8
|
+
|
|
9
|
+
Product repos keep their own manifests and env source files, while this package
|
|
10
|
+
renders provider-specific Terraform workspaces and keeps the existing
|
|
11
|
+
Vercel reconciliation flow available as compatibility commands.
|
|
12
|
+
|
|
13
|
+
## Install
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
pnpm add -D @vertile-ai/iac
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Terraform is required for `vertile-iac plan`. The `render` command does not call
|
|
20
|
+
Terraform and can be used offline.
|
|
21
|
+
|
|
22
|
+
## Commands
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
vertile-iac render --target=all --env=production
|
|
26
|
+
vertile-iac plan --target=vercel --env=preview
|
|
27
|
+
vertile-iac apply --target=aws --env=production --yes
|
|
28
|
+
|
|
29
|
+
vertile-iac sync-env --repo-root ../noop --variants=local,staging,test
|
|
30
|
+
vertile-iac env --repo-root ../noop --scope=all --targets=preview,production
|
|
31
|
+
vertile-iac projects --repo-root ../noop
|
|
32
|
+
vertile-iac domains --repo-root ../noop
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
The `render`, `plan`, and `apply` commands read `infrastructure/iac/iac.json`
|
|
36
|
+
and write generated Terraform workspaces to `.vertile/terraform/<target>/`.
|
|
37
|
+
|
|
38
|
+
Apply is guarded. Non-interactive apply requires `--yes`, which passes
|
|
39
|
+
Terraform `-auto-approve`.
|
|
40
|
+
|
|
41
|
+
The `env`, `projects`, and `domains` commands reconcile Vercel through the
|
|
42
|
+
Vercel API. They still read the older compatibility manifest files when those
|
|
43
|
+
files exist, and otherwise derive the same desired state from
|
|
44
|
+
`infrastructure/iac/iac.json`.
|
|
45
|
+
|
|
46
|
+
Apply mode requires `VERCEL_TOKEN`, `VERCEL_API_KEY`, or a token file:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
VERCEL_TOKEN=... vertile-iac env --repo-root ../noop --apply
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Manifest Layout
|
|
53
|
+
|
|
54
|
+
The new Terraform flow expects the target project to have:
|
|
55
|
+
|
|
56
|
+
```text
|
|
57
|
+
infrastructure/iac/iac.json
|
|
58
|
+
infrastructure/shared/.env.development
|
|
59
|
+
infrastructure/shared/.env.staging
|
|
60
|
+
infrastructure/shared/.env.production
|
|
61
|
+
infrastructure/<app-key>/.env.development
|
|
62
|
+
infrastructure/<app-key>/.env.staging
|
|
63
|
+
infrastructure/<app-key>/.env.production
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Minimal `iac.json` example:
|
|
67
|
+
|
|
68
|
+
```json
|
|
69
|
+
{
|
|
70
|
+
"$schema": "./node_modules/@vertile-ai/iac/schema/iac.schema.json",
|
|
71
|
+
"version": 1,
|
|
72
|
+
"project": { "name": "example" },
|
|
73
|
+
"environments": ["development", "preview", "production"],
|
|
74
|
+
"providers": {
|
|
75
|
+
"vercel": { "team": "example-team" },
|
|
76
|
+
"aws": { "region": "us-east-1" },
|
|
77
|
+
"digitalocean": {}
|
|
78
|
+
},
|
|
79
|
+
"apps": [
|
|
80
|
+
{
|
|
81
|
+
"key": "web",
|
|
82
|
+
"name": "example-web",
|
|
83
|
+
"framework": "nextjs",
|
|
84
|
+
"rootDirectory": "apps/web",
|
|
85
|
+
"domains": ["web.example.com"]
|
|
86
|
+
}
|
|
87
|
+
],
|
|
88
|
+
"env": {
|
|
89
|
+
"sourceDir": "infrastructure",
|
|
90
|
+
"sync": {
|
|
91
|
+
"apps": ["web"]
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
"domains": []
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Portable concepts currently include:
|
|
99
|
+
|
|
100
|
+
- `apps`
|
|
101
|
+
- `domains`
|
|
102
|
+
- `objectStorage`
|
|
103
|
+
- `databases`
|
|
104
|
+
- `queues`
|
|
105
|
+
- `sandboxes`
|
|
106
|
+
- `clusters`
|
|
107
|
+
|
|
108
|
+
Provider-specific Terraform resources can be added under
|
|
109
|
+
`providers.<target>.resources` as `{ "type", "name", "values" }` objects while
|
|
110
|
+
the manifest schema stays narrow.
|
|
111
|
+
|
|
112
|
+
The compatibility Vercel commands expect the target project to have:
|
|
113
|
+
|
|
114
|
+
```text
|
|
115
|
+
infrastructure/shared/.env.development
|
|
116
|
+
infrastructure/shared/.env.staging
|
|
117
|
+
infrastructure/shared/.env.production
|
|
118
|
+
infrastructure/<project-key>/.env.development
|
|
119
|
+
infrastructure/<project-key>/.env.staging
|
|
120
|
+
infrastructure/<project-key>/.env.production
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
When the old compatibility files exist, they are used directly:
|
|
124
|
+
|
|
125
|
+
```text
|
|
126
|
+
infrastructure/iac/env-manifest.json
|
|
127
|
+
infrastructure/iac/project-settings.json
|
|
128
|
+
infrastructure/iac/project-domains.json
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
When they do not exist, the Vercel commands derive equivalent manifests from
|
|
132
|
+
`iac.json`:
|
|
133
|
+
|
|
134
|
+
- `providers.vercel.teamSlug` or `providers.vercel.team` becomes the Vercel team.
|
|
135
|
+
- `env.sourceDir` selects the env source folder and defaults to `infrastructure`.
|
|
136
|
+
- `apps[].key`, `apps[].id` or `apps[].projectId`, and `apps[].name` become managed Vercel projects.
|
|
137
|
+
- `apps[].rootDirectory`, `apps[].nodeVersion`, and
|
|
138
|
+
`apps[].enableAffectedProjectsDeployments` become project settings.
|
|
139
|
+
- `apps[].domains` and top-level `domains[]` become project domains.
|
|
140
|
+
|
|
141
|
+
Vercel targets map to env files as follows:
|
|
142
|
+
|
|
143
|
+
- `development` -> `.env.development`
|
|
144
|
+
- `preview` -> `.env.staging`
|
|
145
|
+
- `production` -> `.env.production`
|
|
146
|
+
|
|
147
|
+
Pure local env files such as `.env.local` are intentionally not synced to
|
|
148
|
+
Vercel. The default manifest location can be overridden:
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
vertile-iac env \
|
|
152
|
+
--repo-root ../some-project \
|
|
153
|
+
--manifest ./deploy/env-manifest.json \
|
|
154
|
+
--infra-dir infrastructure
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## Env File Sync
|
|
158
|
+
|
|
159
|
+
`vertile-iac sync-env` generates package-local `.env.*` files from the env
|
|
160
|
+
source tree declared by `iac.json`. This is separate from `vertile-iac env`,
|
|
161
|
+
which reconciles Vercel remote environment variables.
|
|
162
|
+
|
|
163
|
+
The boundary is:
|
|
164
|
+
|
|
165
|
+
- `env.sourceDir` is the source tree, defaulting to `infrastructure`.
|
|
166
|
+
- `env.sync.sharedKey` is the shared source folder, defaulting to `shared`.
|
|
167
|
+
- `env.sync.apps` limits which `apps[]` are materialized locally. Without it,
|
|
168
|
+
all apps are synced.
|
|
169
|
+
- Each app reads from `<env.sourceDir>/<app.key>` by default.
|
|
170
|
+
- Each app writes into `apps[].rootDirectory` by default.
|
|
171
|
+
- `apps[].env.sourceKey` and `apps[].env.outputDir` override those defaults.
|
|
172
|
+
- `apps[].env.sharedPrefix` projects prefixed shared keys into one app, strips
|
|
173
|
+
the prefix in that app's generated file, and keeps those prefixed keys out of
|
|
174
|
+
other generated app env files.
|
|
175
|
+
|
|
176
|
+
Examples:
|
|
177
|
+
|
|
178
|
+
```json
|
|
179
|
+
{
|
|
180
|
+
"env": {
|
|
181
|
+
"sourceDir": "infrastructure",
|
|
182
|
+
"sync": {
|
|
183
|
+
"apps": ["landing", "web-client", "web-server"],
|
|
184
|
+
"sharedKey": "shared"
|
|
185
|
+
}
|
|
186
|
+
},
|
|
187
|
+
"apps": [
|
|
188
|
+
{
|
|
189
|
+
"key": "web-client",
|
|
190
|
+
"rootDirectory": "packages/web-client",
|
|
191
|
+
"env": { "sharedPrefix": "WEB_CLIENT_" }
|
|
192
|
+
}
|
|
193
|
+
]
|
|
194
|
+
}
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
Supported local sync variants are `local`, `staging`, `preview`, `production`,
|
|
198
|
+
and `test`. `preview` is an alias for `.env.staging`.
|
|
199
|
+
|
|
200
|
+
## Shared Options
|
|
201
|
+
|
|
202
|
+
- `--repo-root <path>`: product repo root containing `infrastructure/`.
|
|
203
|
+
- `--iac-dir <path>`: manifest directory, default `infrastructure/iac`.
|
|
204
|
+
- `--manifest <path>`: env manifest path.
|
|
205
|
+
- `--project-settings <path>`: project settings manifest path.
|
|
206
|
+
- `--project-domains <path>`: project domains manifest path.
|
|
207
|
+
- `--infra-dir <path>`: override `env-manifest.json` `infraDir`.
|
|
208
|
+
- `--token-file <path>`: token file, default `<repo-root>/.vercel.token`.
|
|
209
|
+
- `--auto-create-keys <a,b>`: project keys allowed for Vercel auto-create.
|
|
210
|
+
- `--auto-create-prefixes <a,b>`: project key prefixes allowed for Vercel auto-create.
|
|
211
|
+
- `--iac-manifest <path>`: source-of-truth IaC manifest, default `<iac-dir>/iac.json`.
|
|
212
|
+
- `--out <path>`: generated Terraform root, default `.vertile/terraform`.
|
|
213
|
+
- `--target <name|all>`: `vercel`, `aws`, `digitalocean`, or `all`.
|
|
214
|
+
- `--env <name>`: environment to render, plan, or apply, default `production`.
|
|
215
|
+
- `--terraform-bin <path>`: Terraform executable for `plan`, default `terraform`.
|
|
216
|
+
- `--yes`: allow non-interactive `apply` with Terraform auto-approve.
|
|
217
|
+
|
|
218
|
+
## Docs
|
|
219
|
+
|
|
220
|
+
Public-facing docs live in `docs/`. The static docs website entrypoint is:
|
|
221
|
+
|
|
222
|
+
```text
|
|
223
|
+
docs/index.html
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
## Examples
|
|
227
|
+
|
|
228
|
+
`examples/dynomic` is a publishable fixture project that shows the current app
|
|
229
|
+
shape for Vercel project settings, domains, preview/production env
|
|
230
|
+
provisioning, local package env sync, portable resource concepts, provider
|
|
231
|
+
overrides, and provider-specific escape hatch resources.
|
|
232
|
+
|
|
233
|
+
## Schema
|
|
234
|
+
|
|
235
|
+
The manifest schema is published as JSON Schema Draft 2020-12:
|
|
236
|
+
|
|
237
|
+
```text
|
|
238
|
+
schema/iac.schema.json
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
Product manifests can reference the package copy:
|
|
242
|
+
|
|
243
|
+
```json
|
|
244
|
+
{
|
|
245
|
+
"$schema": "./node_modules/@vertile-ai/iac/schema/iac.schema.json"
|
|
246
|
+
}
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
## Publishing
|
|
250
|
+
|
|
251
|
+
Publish with public access:
|
|
252
|
+
|
|
253
|
+
```bash
|
|
254
|
+
pnpm publish --access public
|
|
255
|
+
```
|
package/docs/README.md
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Vertile AI IaC Docs
|
|
2
|
+
|
|
3
|
+
Vertile AI IaC is an app-first infrastructure abstraction for solo builders and
|
|
4
|
+
small teams. Define infrastructure intent once, then compile it into
|
|
5
|
+
provider-specific infrastructure without adopting Kubernetes as a control plane.
|
|
6
|
+
|
|
7
|
+
## Start Here
|
|
8
|
+
|
|
9
|
+
- [Positioning](./positioning.md)
|
|
10
|
+
- [Roadmap](./roadmap.md)
|
|
11
|
+
- [Manifest Guide](./manifest.md)
|
|
12
|
+
- [Static Website](./index.html)
|
|
13
|
+
|
|
14
|
+
## Core Idea
|
|
15
|
+
|
|
16
|
+
Crossplane is Kubernetes-first platform infrastructure.
|
|
17
|
+
Terraform and OpenTofu are provider-specific execution engines.
|
|
18
|
+
Vertile AI IaC is app-first portable infrastructure intent.
|
|
19
|
+
|
|
20
|
+
The user-authored source of truth is:
|
|
21
|
+
|
|
22
|
+
```text
|
|
23
|
+
infrastructure/iac/iac.json
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Generated Terraform lives under:
|
|
27
|
+
|
|
28
|
+
```text
|
|
29
|
+
.vertile/terraform/<provider>/
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Users edit the manifest. Vertile AI IaC renders provider-specific infrastructure.
|
|
33
|
+
|
|
34
|
+
## Commands
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
vertile-iac render --target=all --env=production
|
|
38
|
+
vertile-iac plan --target=aws --env=production
|
|
39
|
+
vertile-iac apply --target=aws --env=production --yes
|
|
40
|
+
```
|
package/docs/index.html
ADDED
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
+
<title>Vertile AI IaC</title>
|
|
7
|
+
<style>
|
|
8
|
+
:root {
|
|
9
|
+
color-scheme: light;
|
|
10
|
+
--bg: #fbfaf7;
|
|
11
|
+
--ink: #161615;
|
|
12
|
+
--muted: #66615b;
|
|
13
|
+
--line: #ddd7ce;
|
|
14
|
+
--panel: #ffffff;
|
|
15
|
+
--accent: #0f766e;
|
|
16
|
+
--accent-ink: #0b4f4a;
|
|
17
|
+
--code: #f1eee8;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
* {
|
|
21
|
+
box-sizing: border-box;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
body {
|
|
25
|
+
margin: 0;
|
|
26
|
+
background: var(--bg);
|
|
27
|
+
color: var(--ink);
|
|
28
|
+
font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
|
29
|
+
line-height: 1.55;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
header,
|
|
33
|
+
main,
|
|
34
|
+
footer {
|
|
35
|
+
width: min(1080px, calc(100% - 40px));
|
|
36
|
+
margin: 0 auto;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
header {
|
|
40
|
+
min-height: 78vh;
|
|
41
|
+
display: grid;
|
|
42
|
+
align-content: center;
|
|
43
|
+
padding: 56px 0 40px;
|
|
44
|
+
border-bottom: 1px solid var(--line);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
nav {
|
|
48
|
+
position: absolute;
|
|
49
|
+
top: 20px;
|
|
50
|
+
left: 50%;
|
|
51
|
+
transform: translateX(-50%);
|
|
52
|
+
width: min(1080px, calc(100% - 40px));
|
|
53
|
+
display: flex;
|
|
54
|
+
align-items: center;
|
|
55
|
+
justify-content: space-between;
|
|
56
|
+
gap: 20px;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
nav a {
|
|
60
|
+
color: var(--muted);
|
|
61
|
+
text-decoration: none;
|
|
62
|
+
font-size: 14px;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.brand {
|
|
66
|
+
color: var(--ink);
|
|
67
|
+
font-weight: 700;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.links {
|
|
71
|
+
display: flex;
|
|
72
|
+
gap: 18px;
|
|
73
|
+
flex-wrap: wrap;
|
|
74
|
+
justify-content: flex-end;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
h1 {
|
|
78
|
+
max-width: 820px;
|
|
79
|
+
margin: 0;
|
|
80
|
+
font-size: clamp(46px, 8vw, 90px);
|
|
81
|
+
line-height: 0.98;
|
|
82
|
+
letter-spacing: 0;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.lede {
|
|
86
|
+
max-width: 720px;
|
|
87
|
+
margin: 26px 0 0;
|
|
88
|
+
color: var(--muted);
|
|
89
|
+
font-size: clamp(18px, 2.3vw, 24px);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
.actions {
|
|
93
|
+
display: flex;
|
|
94
|
+
gap: 14px;
|
|
95
|
+
flex-wrap: wrap;
|
|
96
|
+
margin-top: 34px;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
.button {
|
|
100
|
+
display: inline-flex;
|
|
101
|
+
min-height: 44px;
|
|
102
|
+
align-items: center;
|
|
103
|
+
justify-content: center;
|
|
104
|
+
border: 1px solid var(--ink);
|
|
105
|
+
border-radius: 6px;
|
|
106
|
+
padding: 10px 16px;
|
|
107
|
+
color: var(--ink);
|
|
108
|
+
text-decoration: none;
|
|
109
|
+
font-weight: 650;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
.button.primary {
|
|
113
|
+
border-color: var(--accent);
|
|
114
|
+
background: var(--accent);
|
|
115
|
+
color: #fff;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
main {
|
|
119
|
+
padding: 42px 0 64px;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
section {
|
|
123
|
+
padding: 34px 0;
|
|
124
|
+
border-bottom: 1px solid var(--line);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
h2 {
|
|
128
|
+
margin: 0 0 14px;
|
|
129
|
+
font-size: 28px;
|
|
130
|
+
line-height: 1.15;
|
|
131
|
+
letter-spacing: 0;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
p {
|
|
135
|
+
max-width: 780px;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
.grid {
|
|
139
|
+
display: grid;
|
|
140
|
+
grid-template-columns: repeat(3, minmax(0, 1fr));
|
|
141
|
+
gap: 14px;
|
|
142
|
+
margin-top: 22px;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
.tile {
|
|
146
|
+
min-height: 160px;
|
|
147
|
+
border: 1px solid var(--line);
|
|
148
|
+
border-radius: 8px;
|
|
149
|
+
background: var(--panel);
|
|
150
|
+
padding: 18px;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
.tile h3 {
|
|
154
|
+
margin: 0 0 8px;
|
|
155
|
+
font-size: 18px;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
.tile p {
|
|
159
|
+
margin: 0;
|
|
160
|
+
color: var(--muted);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
pre {
|
|
164
|
+
overflow: auto;
|
|
165
|
+
border: 1px solid var(--line);
|
|
166
|
+
border-radius: 8px;
|
|
167
|
+
background: var(--code);
|
|
168
|
+
padding: 18px;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
code {
|
|
172
|
+
font-family: "SFMono-Regular", Consolas, "Liberation Mono", monospace;
|
|
173
|
+
font-size: 0.94em;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
footer {
|
|
177
|
+
padding: 28px 0 42px;
|
|
178
|
+
color: var(--muted);
|
|
179
|
+
font-size: 14px;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
@media (max-width: 760px) {
|
|
183
|
+
header {
|
|
184
|
+
min-height: 84vh;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
nav {
|
|
188
|
+
align-items: flex-start;
|
|
189
|
+
flex-direction: column;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
.links {
|
|
193
|
+
justify-content: flex-start;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
.grid {
|
|
197
|
+
grid-template-columns: 1fr;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
</style>
|
|
201
|
+
</head>
|
|
202
|
+
<body>
|
|
203
|
+
<nav>
|
|
204
|
+
<a class="brand" href="#top">Vertile AI IaC</a>
|
|
205
|
+
<div class="links">
|
|
206
|
+
<a href="#manifest">Manifest</a>
|
|
207
|
+
<a href="#providers">Providers</a>
|
|
208
|
+
<a href="#roadmap">Roadmap</a>
|
|
209
|
+
</div>
|
|
210
|
+
</nav>
|
|
211
|
+
|
|
212
|
+
<header id="top">
|
|
213
|
+
<h1>App-first infrastructure intent.</h1>
|
|
214
|
+
<p class="lede">Define infrastructure once, then render, plan, and apply provider-specific Terraform for Vercel, AWS, DigitalOcean, and future app infrastructure providers.</p>
|
|
215
|
+
<div class="actions">
|
|
216
|
+
<a class="button primary" href="#manifest">Read the manifest guide</a>
|
|
217
|
+
<a class="button" href="./roadmap.md">View roadmap</a>
|
|
218
|
+
</div>
|
|
219
|
+
</header>
|
|
220
|
+
|
|
221
|
+
<main>
|
|
222
|
+
<section id="manifest">
|
|
223
|
+
<h2>One Source Of Truth</h2>
|
|
224
|
+
<p>The user-authored source of truth is <code>infrastructure/iac/iac.json</code>. Terraform files are generated output under <code>.vertile/terraform/<provider>/</code>.</p>
|
|
225
|
+
<pre><code>{
|
|
226
|
+
"version": 1,
|
|
227
|
+
"project": { "name": "example" },
|
|
228
|
+
"environments": ["development", "preview", "production"],
|
|
229
|
+
"providers": {
|
|
230
|
+
"vercel": { "team": "example-team" },
|
|
231
|
+
"aws": { "region": "us-east-1" },
|
|
232
|
+
"digitalocean": {}
|
|
233
|
+
},
|
|
234
|
+
"apps": [{ "key": "web", "domains": ["web.example.com"] }],
|
|
235
|
+
"objectStorage": [{ "key": "uploads" }],
|
|
236
|
+
"databases": [{ "key": "appdb", "engine": "postgres" }],
|
|
237
|
+
"queues": [{ "key": "jobs" }],
|
|
238
|
+
"sandboxes": [{ "key": "runner" }],
|
|
239
|
+
"clusters": [{ "key": "workers", "size": 2 }]
|
|
240
|
+
}</code></pre>
|
|
241
|
+
</section>
|
|
242
|
+
|
|
243
|
+
<section id="providers">
|
|
244
|
+
<h2>Provider Adapters</h2>
|
|
245
|
+
<p>Vertile AI IaC keeps portable concepts in the manifest and compiles them into provider-specific resources. Provider overrides are available when a platform has unique behavior.</p>
|
|
246
|
+
<div class="grid">
|
|
247
|
+
<div class="tile">
|
|
248
|
+
<h3>Vercel</h3>
|
|
249
|
+
<p>Apps, domains, env vars, and project settings for frontend and serverless deployments.</p>
|
|
250
|
+
</div>
|
|
251
|
+
<div class="tile">
|
|
252
|
+
<h3>AWS</h3>
|
|
253
|
+
<p>Object storage, databases, queues, compute, and clusters for broader infrastructure needs.</p>
|
|
254
|
+
</div>
|
|
255
|
+
<div class="tile">
|
|
256
|
+
<h3>DigitalOcean</h3>
|
|
257
|
+
<p>Spaces, managed databases, app hosting, droplets, and pragmatic compute defaults.</p>
|
|
258
|
+
</div>
|
|
259
|
+
</div>
|
|
260
|
+
</section>
|
|
261
|
+
|
|
262
|
+
<section id="roadmap">
|
|
263
|
+
<h2>Built For Small Teams And AI Workflows</h2>
|
|
264
|
+
<p>Crossplane makes Kubernetes the infrastructure control plane. Vertile AI IaC uses a Git repo and CLI as the control surface, which is lighter for solo builders and app-first teams.</p>
|
|
265
|
+
<pre><code>pnpm add -D @vertile-ai/iac
|
|
266
|
+
vertile-iac render --target=all --env=production
|
|
267
|
+
vertile-iac plan --target=aws --env=production
|
|
268
|
+
vertile-iac apply --target=aws --env=production --yes</code></pre>
|
|
269
|
+
</section>
|
|
270
|
+
</main>
|
|
271
|
+
|
|
272
|
+
<footer>
|
|
273
|
+
Crossplane is Kubernetes-first platform infrastructure. Terraform/OpenTofu is provider-specific execution. Vertile AI IaC is app-first portable infrastructure intent.
|
|
274
|
+
</footer>
|
|
275
|
+
</body>
|
|
276
|
+
</html>
|
package/docs/manifest.md
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# Manifest Guide
|
|
2
|
+
|
|
3
|
+
The manifest is the source of truth for app infrastructure intent.
|
|
4
|
+
|
|
5
|
+
```text
|
|
6
|
+
infrastructure/iac/iac.json
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
Generated Terraform is an implementation detail:
|
|
10
|
+
|
|
11
|
+
```text
|
|
12
|
+
.vertile/terraform/vercel/
|
|
13
|
+
.vertile/terraform/aws/
|
|
14
|
+
.vertile/terraform/digitalocean/
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Minimal Manifest
|
|
18
|
+
|
|
19
|
+
```json
|
|
20
|
+
{
|
|
21
|
+
"$schema": "./node_modules/@vertile-ai/iac/schema/iac.schema.json",
|
|
22
|
+
"version": 1,
|
|
23
|
+
"project": { "name": "example" },
|
|
24
|
+
"environments": ["development", "preview", "production"],
|
|
25
|
+
"providers": {
|
|
26
|
+
"vercel": { "team": "example-team" },
|
|
27
|
+
"aws": { "region": "us-east-1" },
|
|
28
|
+
"digitalocean": {}
|
|
29
|
+
},
|
|
30
|
+
"apps": [
|
|
31
|
+
{
|
|
32
|
+
"key": "web",
|
|
33
|
+
"name": "example-web",
|
|
34
|
+
"framework": "nextjs",
|
|
35
|
+
"rootDirectory": "apps/web",
|
|
36
|
+
"domains": ["web.example.com"]
|
|
37
|
+
}
|
|
38
|
+
],
|
|
39
|
+
"domains": [],
|
|
40
|
+
"objectStorage": [{ "key": "uploads", "visibility": "private" }],
|
|
41
|
+
"databases": [{ "key": "appdb", "engine": "postgres" }],
|
|
42
|
+
"queues": [{ "key": "jobs" }],
|
|
43
|
+
"sandboxes": [{ "key": "runner" }],
|
|
44
|
+
"clusters": [{ "key": "workers", "size": 2 }]
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
The schema is published as JSON Schema Draft 2020-12 at
|
|
49
|
+
`schema/iac.schema.json`.
|
|
50
|
+
|
|
51
|
+
## Provider Overrides
|
|
52
|
+
|
|
53
|
+
Portable concepts should be shared by default, with provider-specific overrides
|
|
54
|
+
only where the provider really differs.
|
|
55
|
+
|
|
56
|
+
```json
|
|
57
|
+
{
|
|
58
|
+
"objectStorage": [
|
|
59
|
+
{
|
|
60
|
+
"key": "assets",
|
|
61
|
+
"visibility": "private",
|
|
62
|
+
"providers": {
|
|
63
|
+
"aws": { "storageClass": "standard" },
|
|
64
|
+
"digitalocean": { "region": "nyc3" }
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
]
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Escape Hatch
|
|
72
|
+
|
|
73
|
+
Provider-specific Terraform resources can be expressed under
|
|
74
|
+
`providers.<target>.resources` while the portable schema matures.
|
|
75
|
+
|
|
76
|
+
```json
|
|
77
|
+
{
|
|
78
|
+
"providers": {
|
|
79
|
+
"aws": {
|
|
80
|
+
"resources": [
|
|
81
|
+
{
|
|
82
|
+
"type": "aws_s3_bucket",
|
|
83
|
+
"name": "assets",
|
|
84
|
+
"values": {
|
|
85
|
+
"bucket": "example-assets"
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
]
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Use this as a bridge, not as the primary authoring model. The long-term goal is
|
|
95
|
+
to promote common patterns into first-class portable concepts.
|
|
96
|
+
|
|
97
|
+
## Supported Concepts
|
|
98
|
+
|
|
99
|
+
| Concept | Vercel | AWS | DigitalOcean |
|
|
100
|
+
| --- | --- | --- | --- |
|
|
101
|
+
| `apps` | Vercel Project | - | - |
|
|
102
|
+
| `domains` | Vercel Project Domain | - | - |
|
|
103
|
+
| `objectStorage` | - | S3 Bucket | Spaces Bucket |
|
|
104
|
+
| `databases` | - | RDS Instance | Managed Database Cluster |
|
|
105
|
+
| `queues` | - | SQS Queue | - |
|
|
106
|
+
| `sandboxes` | - | EC2 Instance | Droplet |
|
|
107
|
+
| `clusters` | - | EC2 Instance group | Droplet group |
|
|
108
|
+
|
|
109
|
+
Unsupported provider cells are intentionally blank. Use provider-specific
|
|
110
|
+
resources or another provider for those capabilities.
|
|
111
|
+
|
|
112
|
+
## Commands
|
|
113
|
+
|
|
114
|
+
Render generated Terraform:
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
vertile-iac render --target=all --env=production
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Preview changes with Terraform:
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
vertile-iac plan --target=aws --env=production
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Apply changes with explicit non-interactive approval:
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
vertile-iac apply --target=aws --env=production --yes
|
|
130
|
+
```
|