@specific.dev/cli 0.1.37
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/dist/admin/404/index.html +1 -0
- package/dist/admin/404.html +1 -0
- package/dist/admin/__next.__PAGE__.txt +9 -0
- package/dist/admin/__next._full.txt +20 -0
- package/dist/admin/__next._head.txt +6 -0
- package/dist/admin/__next._index.txt +5 -0
- package/dist/admin/__next._tree.txt +4 -0
- package/dist/admin/_next/static/0zkv3YeV6IWOWVWE1S1A1/_buildManifest.js +11 -0
- package/dist/admin/_next/static/0zkv3YeV6IWOWVWE1S1A1/_clientMiddlewareManifest.json +1 -0
- package/dist/admin/_next/static/0zkv3YeV6IWOWVWE1S1A1/_ssgManifest.js +1 -0
- package/dist/admin/_next/static/1o2O2QPOKhuZyxcNaCjq8/_buildManifest.js +11 -0
- package/dist/admin/_next/static/1o2O2QPOKhuZyxcNaCjq8/_clientMiddlewareManifest.json +1 -0
- package/dist/admin/_next/static/1o2O2QPOKhuZyxcNaCjq8/_ssgManifest.js +1 -0
- package/dist/admin/_next/static/2ZtqtFX2EkuM82mTM7NWT/_buildManifest.js +11 -0
- package/dist/admin/_next/static/2ZtqtFX2EkuM82mTM7NWT/_clientMiddlewareManifest.json +1 -0
- package/dist/admin/_next/static/2ZtqtFX2EkuM82mTM7NWT/_ssgManifest.js +1 -0
- package/dist/admin/_next/static/2vLF5bo4ZtUfhs8bdQBoF/_buildManifest.js +11 -0
- package/dist/admin/_next/static/2vLF5bo4ZtUfhs8bdQBoF/_clientMiddlewareManifest.json +1 -0
- package/dist/admin/_next/static/2vLF5bo4ZtUfhs8bdQBoF/_ssgManifest.js +1 -0
- package/dist/admin/_next/static/8_8s51m7RbLVvbIechG-b/_buildManifest.js +11 -0
- package/dist/admin/_next/static/8_8s51m7RbLVvbIechG-b/_clientMiddlewareManifest.json +1 -0
- package/dist/admin/_next/static/8_8s51m7RbLVvbIechG-b/_ssgManifest.js +1 -0
- package/dist/admin/_next/static/C4Y44KtLnP684m57gN3Ga/_buildManifest.js +11 -0
- package/dist/admin/_next/static/C4Y44KtLnP684m57gN3Ga/_clientMiddlewareManifest.json +1 -0
- package/dist/admin/_next/static/C4Y44KtLnP684m57gN3Ga/_ssgManifest.js +1 -0
- package/dist/admin/_next/static/DBeh36kAJnZBeBMJSPBW8/_buildManifest.js +11 -0
- package/dist/admin/_next/static/DBeh36kAJnZBeBMJSPBW8/_clientMiddlewareManifest.json +1 -0
- package/dist/admin/_next/static/DBeh36kAJnZBeBMJSPBW8/_ssgManifest.js +1 -0
- package/dist/admin/_next/static/REKEUu6DP2t99jKSAqNOu/_buildManifest.js +11 -0
- package/dist/admin/_next/static/REKEUu6DP2t99jKSAqNOu/_clientMiddlewareManifest.json +1 -0
- package/dist/admin/_next/static/REKEUu6DP2t99jKSAqNOu/_ssgManifest.js +1 -0
- package/dist/admin/_next/static/TDGT3mkKtTe0w_CxujLxI/_buildManifest.js +11 -0
- package/dist/admin/_next/static/TDGT3mkKtTe0w_CxujLxI/_clientMiddlewareManifest.json +1 -0
- package/dist/admin/_next/static/TDGT3mkKtTe0w_CxujLxI/_ssgManifest.js +1 -0
- package/dist/admin/_next/static/ZHMJ-g7mAlAMt_2uCXeEk/_buildManifest.js +11 -0
- package/dist/admin/_next/static/ZHMJ-g7mAlAMt_2uCXeEk/_clientMiddlewareManifest.json +1 -0
- package/dist/admin/_next/static/ZHMJ-g7mAlAMt_2uCXeEk/_ssgManifest.js +1 -0
- package/dist/admin/_next/static/a6JDAB-CdvWPvS4sBgJji/_buildManifest.js +11 -0
- package/dist/admin/_next/static/a6JDAB-CdvWPvS4sBgJji/_clientMiddlewareManifest.json +1 -0
- package/dist/admin/_next/static/a6JDAB-CdvWPvS4sBgJji/_ssgManifest.js +1 -0
- package/dist/admin/_next/static/abFUeBpymhlx-IxygVnM9/_buildManifest.js +11 -0
- package/dist/admin/_next/static/abFUeBpymhlx-IxygVnM9/_clientMiddlewareManifest.json +1 -0
- package/dist/admin/_next/static/abFUeBpymhlx-IxygVnM9/_ssgManifest.js +1 -0
- package/dist/admin/_next/static/chunks/0afa5e999b6c353a.js +1 -0
- package/dist/admin/_next/static/chunks/1584f10ea1cebcb2.js +4 -0
- package/dist/admin/_next/static/chunks/195bbec70cfcd241.js +2 -0
- package/dist/admin/_next/static/chunks/2583656ea9ac4ad6.js +5 -0
- package/dist/admin/_next/static/chunks/465f799faf41e6df.js +1 -0
- package/dist/admin/_next/static/chunks/4ab079bdcb131778.js +5 -0
- package/dist/admin/_next/static/chunks/4b5ae5ebb2087f0d.js +2 -0
- package/dist/admin/_next/static/chunks/605800ff25160d05.js +1 -0
- package/dist/admin/_next/static/chunks/656b870f0567ed5f.js +1 -0
- package/dist/admin/_next/static/chunks/71098a6cd6181738.css +3 -0
- package/dist/admin/_next/static/chunks/806bdb8e4a6a9b95.js +4 -0
- package/dist/admin/_next/static/chunks/895a6f91f0b479fb.js +2 -0
- package/dist/admin/_next/static/chunks/9032f4a1aac1ca5d.css +3 -0
- package/dist/admin/_next/static/chunks/a28af2dc6f5fbaad.js +1 -0
- package/dist/admin/_next/static/chunks/a6dad97d9634a72d.js +1 -0
- package/dist/admin/_next/static/chunks/a6dad97d9634a72d.js.map +1 -0
- package/dist/admin/_next/static/chunks/b4205fc2f84bda68.css +3 -0
- package/dist/admin/_next/static/chunks/c1a750c25bc8d092.js +1 -0
- package/dist/admin/_next/static/chunks/c3d30f6f144dca51.js +2 -0
- package/dist/admin/_next/static/chunks/cbf55ce8731457ae.js +2 -0
- package/dist/admin/_next/static/chunks/d2be314c3ece3fbe.js +1 -0
- package/dist/admin/_next/static/chunks/de6af6d8adf8b50a.js +5 -0
- package/dist/admin/_next/static/chunks/f0c001244d275aab.js +5 -0
- package/dist/admin/_next/static/chunks/fde89fd76ad6a3d0.css +3 -0
- package/dist/admin/_next/static/chunks/ff1a16fafef87110.js +1 -0
- package/dist/admin/_next/static/chunks/turbopack-a3d691c83d4b1778.js +4 -0
- package/dist/admin/_next/static/chunks/turbopack-e5185af8e7c716ca.js +4 -0
- package/dist/admin/_next/static/dcjrfD44GuB6g8bZ6BcFm/_buildManifest.js +11 -0
- package/dist/admin/_next/static/dcjrfD44GuB6g8bZ6BcFm/_clientMiddlewareManifest.json +1 -0
- package/dist/admin/_next/static/dcjrfD44GuB6g8bZ6BcFm/_ssgManifest.js +1 -0
- package/dist/admin/_next/static/fTEa8Scx922n-dMuqN3Vc/_buildManifest.js +11 -0
- package/dist/admin/_next/static/fTEa8Scx922n-dMuqN3Vc/_clientMiddlewareManifest.json +1 -0
- package/dist/admin/_next/static/fTEa8Scx922n-dMuqN3Vc/_ssgManifest.js +1 -0
- package/dist/admin/_next/static/kZNwyhHft01wPtJ_AvqQT/_buildManifest.js +11 -0
- package/dist/admin/_next/static/kZNwyhHft01wPtJ_AvqQT/_clientMiddlewareManifest.json +1 -0
- package/dist/admin/_next/static/kZNwyhHft01wPtJ_AvqQT/_ssgManifest.js +1 -0
- package/dist/admin/_next/static/kyVInC6N3DY6NWnjkLQ4J/_buildManifest.js +11 -0
- package/dist/admin/_next/static/kyVInC6N3DY6NWnjkLQ4J/_clientMiddlewareManifest.json +1 -0
- package/dist/admin/_next/static/kyVInC6N3DY6NWnjkLQ4J/_ssgManifest.js +1 -0
- package/dist/admin/_next/static/media/4fa387ec64143e14-s.c1fdd6c2.woff2 +0 -0
- package/dist/admin/_next/static/media/7178b3e590c64307-s.b97b3418.woff2 +0 -0
- package/dist/admin/_next/static/media/797e433ab948586e-s.p.dbea232f.woff2 +0 -0
- package/dist/admin/_next/static/media/8a480f0b521d4e75-s.8e0177b5.woff2 +0 -0
- package/dist/admin/_next/static/media/bbc41e54d2fcbd21-s.799d8ef8.woff2 +0 -0
- package/dist/admin/_next/static/media/caa3a2e1cccd8315-s.p.853070df.woff2 +0 -0
- package/dist/admin/_next/static/media/favicon.0b3bf435.ico +0 -0
- package/dist/admin/_next/static/twedXBdhyxpjS68UQoNAc/_buildManifest.js +11 -0
- package/dist/admin/_next/static/twedXBdhyxpjS68UQoNAc/_clientMiddlewareManifest.json +1 -0
- package/dist/admin/_next/static/twedXBdhyxpjS68UQoNAc/_ssgManifest.js +1 -0
- package/dist/admin/_not-found/__next._full.txt +14 -0
- package/dist/admin/_not-found/__next._head.txt +6 -0
- package/dist/admin/_not-found/__next._index.txt +5 -0
- package/dist/admin/_not-found/__next._not-found.__PAGE__.txt +5 -0
- package/dist/admin/_not-found/__next._not-found.txt +4 -0
- package/dist/admin/_not-found/__next._tree.txt +2 -0
- package/dist/admin/_not-found/index.html +1 -0
- package/dist/admin/_not-found/index.txt +14 -0
- package/dist/admin/databases/__next._full.txt +20 -0
- package/dist/admin/databases/__next._head.txt +6 -0
- package/dist/admin/databases/__next._index.txt +5 -0
- package/dist/admin/databases/__next._tree.txt +4 -0
- package/dist/admin/databases/__next.databases.__PAGE__.txt +9 -0
- package/dist/admin/databases/__next.databases.txt +4 -0
- package/dist/admin/databases/index.html +1 -0
- package/dist/admin/databases/index.txt +20 -0
- package/dist/admin/favicon.ico +0 -0
- package/dist/admin/file.svg +1 -0
- package/dist/admin/globe.svg +1 -0
- package/dist/admin/index.html +1 -0
- package/dist/admin/index.txt +20 -0
- package/dist/admin/next.svg +1 -0
- package/dist/admin/vercel.svg +1 -0
- package/dist/admin/window.svg +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +190523 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/check.d.ts +2 -0
- package/dist/commands/check.d.ts.map +1 -0
- package/dist/commands/check.js +104 -0
- package/dist/commands/check.js.map +1 -0
- package/dist/commands/clean.d.ts +2 -0
- package/dist/commands/clean.d.ts.map +1 -0
- package/dist/commands/clean.js +70 -0
- package/dist/commands/clean.js.map +1 -0
- package/dist/commands/deploy.d.ts +2 -0
- package/dist/commands/deploy.d.ts.map +1 -0
- package/dist/commands/deploy.js +11 -0
- package/dist/commands/deploy.js.map +1 -0
- package/dist/commands/dev.d.ts +2 -0
- package/dist/commands/dev.d.ts.map +1 -0
- package/dist/commands/dev.js +398 -0
- package/dist/commands/dev.js.map +1 -0
- package/dist/commands/docs.d.ts +2 -0
- package/dist/commands/docs.d.ts.map +1 -0
- package/dist/commands/docs.js +32 -0
- package/dist/commands/docs.js.map +1 -0
- package/dist/commands/exec.d.ts +2 -0
- package/dist/commands/exec.d.ts.map +1 -0
- package/dist/commands/exec.js +178 -0
- package/dist/commands/exec.js.map +1 -0
- package/dist/commands/init.d.ts +2 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +339 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/psql.d.ts +2 -0
- package/dist/commands/psql.d.ts.map +1 -0
- package/dist/commands/psql.js +53 -0
- package/dist/commands/psql.js.map +1 -0
- package/dist/commands/secrets.d.ts +3 -0
- package/dist/commands/secrets.d.ts.map +1 -0
- package/dist/commands/secrets.js +136 -0
- package/dist/commands/secrets.js.map +1 -0
- package/dist/docs/builds.md +71 -0
- package/dist/docs/index.md +30 -0
- package/dist/docs/integrations/drizzle.md +83 -0
- package/dist/docs/integrations/nextjs.md +45 -0
- package/dist/docs/integrations/prisma.md +110 -0
- package/dist/docs/postgres.md +41 -0
- package/dist/docs/redis.md +33 -0
- package/dist/docs/secrets-config.md +141 -0
- package/dist/docs/services.md +325 -0
- package/dist/docs/storage.md +62 -0
- package/dist/docs/sync.md +66 -0
- package/dist/lib/dev/database-manager.d.ts +17 -0
- package/dist/lib/dev/database-manager.d.ts.map +1 -0
- package/dist/lib/dev/database-manager.js +114 -0
- package/dist/lib/dev/database-manager.js.map +1 -0
- package/dist/lib/dev/env-resolver.d.ts +14 -0
- package/dist/lib/dev/env-resolver.d.ts.map +1 -0
- package/dist/lib/dev/env-resolver.js +109 -0
- package/dist/lib/dev/env-resolver.js.map +1 -0
- package/dist/lib/dev/http-proxy.d.ts +11 -0
- package/dist/lib/dev/http-proxy.d.ts.map +1 -0
- package/dist/lib/dev/http-proxy.js +165 -0
- package/dist/lib/dev/http-proxy.js.map +1 -0
- package/dist/lib/dev/index.d.ts +11 -0
- package/dist/lib/dev/index.d.ts.map +1 -0
- package/dist/lib/dev/index.js +7 -0
- package/dist/lib/dev/index.js.map +1 -0
- package/dist/lib/dev/instance-state.d.ts +45 -0
- package/dist/lib/dev/instance-state.d.ts.map +1 -0
- package/dist/lib/dev/instance-state.js +213 -0
- package/dist/lib/dev/instance-state.js.map +1 -0
- package/dist/lib/dev/port-allocator.d.ts +5 -0
- package/dist/lib/dev/port-allocator.d.ts.map +1 -0
- package/dist/lib/dev/port-allocator.js +21 -0
- package/dist/lib/dev/port-allocator.js.map +1 -0
- package/dist/lib/dev/service-runner.d.ts +16 -0
- package/dist/lib/dev/service-runner.d.ts.map +1 -0
- package/dist/lib/dev/service-runner.js +61 -0
- package/dist/lib/dev/service-runner.js.map +1 -0
- package/dist/lib/secrets/index.d.ts +2 -0
- package/dist/lib/secrets/index.d.ts.map +1 -0
- package/dist/lib/secrets/index.js +2 -0
- package/dist/lib/secrets/index.js.map +1 -0
- package/dist/lib/secrets/parser.d.ts +37 -0
- package/dist/lib/secrets/parser.d.ts.map +1 -0
- package/dist/lib/secrets/parser.js +116 -0
- package/dist/lib/secrets/parser.js.map +1 -0
- package/package.json +54 -0
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# Secrets and Configuration
|
|
2
|
+
|
|
3
|
+
Secrets and config let you parameterize values that services need. Use them to avoid hardcoding values in your `specific.hcl`.
|
|
4
|
+
|
|
5
|
+
## When to use which
|
|
6
|
+
|
|
7
|
+
- **Secrets** - For sensitive information that should never be committed to version control: API keys, database passwords, signing keys, etc.
|
|
8
|
+
- **Config** - For non-sensitive values that may vary between environments: log levels, feature flags, URLs, etc.
|
|
9
|
+
|
|
10
|
+
## Secrets
|
|
11
|
+
|
|
12
|
+
Declare secrets that your application needs. Values are stored separately from configuration in `specific.secrets` (gitignored).
|
|
13
|
+
|
|
14
|
+
```hcl
|
|
15
|
+
secret "stripe_api_key" {}
|
|
16
|
+
|
|
17
|
+
secret "jwt_secret" {
|
|
18
|
+
generated = true
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
service "api" {
|
|
22
|
+
build = build.api
|
|
23
|
+
command = "./api"
|
|
24
|
+
|
|
25
|
+
endpoint {
|
|
26
|
+
public = true
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
env = {
|
|
30
|
+
STRIPE_API_KEY = secret.stripe_api_key
|
|
31
|
+
JWT_SECRET = secret.jwt_secret
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### Secret fields
|
|
37
|
+
|
|
38
|
+
- `generated` - When `true`, auto-generates a random 64-character string if not manually set. Useful for internal secrets like JWT signing keys.
|
|
39
|
+
- `length` - Custom length for generated secrets (default: 64). Only applies when `generated = true`.
|
|
40
|
+
|
|
41
|
+
### Setting secret values
|
|
42
|
+
|
|
43
|
+
For local development with `specific dev` or `specific exec`, use the CLI:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
specific secrets set stripe_api_key
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
This prompts for the value interactively and stores it in `specific.secrets`. Production secrets are configured separately during deployment.
|
|
50
|
+
|
|
51
|
+
### Generated vs manual secrets
|
|
52
|
+
|
|
53
|
+
- **Manual secrets** (no `generated` flag) - Must be set via `specific secrets set`. Error on startup if missing.
|
|
54
|
+
- **Generated secrets** (`generated = true`) - Auto-created on first run if not set. You can still override manually.
|
|
55
|
+
|
|
56
|
+
## Config
|
|
57
|
+
|
|
58
|
+
Parameterize non-sensitive configuration values across environments.
|
|
59
|
+
|
|
60
|
+
```hcl
|
|
61
|
+
config "log_level" {
|
|
62
|
+
default = "info"
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
service "api" {
|
|
66
|
+
build = build.api
|
|
67
|
+
command = "./api"
|
|
68
|
+
|
|
69
|
+
endpoint {
|
|
70
|
+
public = true
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
env = {
|
|
74
|
+
LOG_LEVEL = config.log_level
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Config fields
|
|
80
|
+
|
|
81
|
+
- `default` - Default value used if not overridden by an environment.
|
|
82
|
+
|
|
83
|
+
### Environment overrides
|
|
84
|
+
|
|
85
|
+
Override config values per environment:
|
|
86
|
+
|
|
87
|
+
```hcl
|
|
88
|
+
config "log_level" {
|
|
89
|
+
default = "info"
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
environment "production" {
|
|
93
|
+
config = {
|
|
94
|
+
log_level = "warn"
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
environment "staging" {
|
|
99
|
+
config = {
|
|
100
|
+
log_level = "debug"
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Example
|
|
106
|
+
|
|
107
|
+
```hcl
|
|
108
|
+
# External API key - must be set manually, sensitive
|
|
109
|
+
secret "stripe_api_key" {}
|
|
110
|
+
|
|
111
|
+
# Internal signing key - auto-generate, sensitive
|
|
112
|
+
secret "jwt_secret" {
|
|
113
|
+
generated = true
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
# Log level - not sensitive, varies by environment
|
|
117
|
+
config "log_level" {
|
|
118
|
+
default = "info"
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
service "api" {
|
|
122
|
+
build = build.api
|
|
123
|
+
command = "./api"
|
|
124
|
+
|
|
125
|
+
endpoint {
|
|
126
|
+
public = true
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
env = {
|
|
130
|
+
STRIPE_API_KEY = secret.stripe_api_key
|
|
131
|
+
JWT_SECRET = secret.jwt_secret
|
|
132
|
+
LOG_LEVEL = config.log_level
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
environment "production" {
|
|
137
|
+
config = {
|
|
138
|
+
log_level = "warn"
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
```
|
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
# Services
|
|
2
|
+
|
|
3
|
+
Services define how to run the code and connect it to other parts of the infrastructure, both in production and in local development. All configuration for the app is handled through environment variables, which the code should read from.
|
|
4
|
+
|
|
5
|
+
## Dynamic services
|
|
6
|
+
|
|
7
|
+
Run a server process exposed via HTTP. TLS is handled automatically.
|
|
8
|
+
|
|
9
|
+
```hcl
|
|
10
|
+
build "api" {
|
|
11
|
+
base = "go"
|
|
12
|
+
command = "go build -o api"
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
service "api" {
|
|
16
|
+
build = build.api
|
|
17
|
+
command = "./api"
|
|
18
|
+
|
|
19
|
+
endpoint {
|
|
20
|
+
public = true
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
env = {
|
|
24
|
+
PORT = port
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
- `command` - Command to start the server
|
|
30
|
+
- `endpoint` - Defines a network endpoint (see Endpoints below)
|
|
31
|
+
- `port` - Reference to the auto-assigned port (pass this to your server)
|
|
32
|
+
|
|
33
|
+
## Endpoints
|
|
34
|
+
|
|
35
|
+
Endpoints define how a service is accessible over the network.
|
|
36
|
+
|
|
37
|
+
### Single endpoint (most common)
|
|
38
|
+
|
|
39
|
+
```hcl
|
|
40
|
+
service "api" {
|
|
41
|
+
build = build.api
|
|
42
|
+
command = "./api"
|
|
43
|
+
|
|
44
|
+
endpoint {
|
|
45
|
+
public = true
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
env = {
|
|
49
|
+
PORT = port
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
- `public = true` - Makes the endpoint publicly accessible via HTTPS
|
|
55
|
+
- Omitting `public` (or `public = false`) keeps the endpoint internal-only
|
|
56
|
+
|
|
57
|
+
### Multiple named endpoints
|
|
58
|
+
|
|
59
|
+
A service can expose multiple endpoints on different ports:
|
|
60
|
+
|
|
61
|
+
```hcl
|
|
62
|
+
service "api" {
|
|
63
|
+
build = build.api
|
|
64
|
+
command = "./api"
|
|
65
|
+
|
|
66
|
+
endpoint "main" {
|
|
67
|
+
public = true
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
endpoint "admin" {}
|
|
71
|
+
|
|
72
|
+
env = {
|
|
73
|
+
MAIN_PORT = endpoint.main.port
|
|
74
|
+
ADMIN_PORT = endpoint.admin.port
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
When using multiple endpoints, you must use `endpoint.<name>.port` instead of `port` to reference each endpoint's port.
|
|
80
|
+
|
|
81
|
+
### Implicit endpoints
|
|
82
|
+
|
|
83
|
+
If a service uses `port` in its env vars but has no explicit endpoint blocks, an implicit internal endpoint is created:
|
|
84
|
+
|
|
85
|
+
```hcl
|
|
86
|
+
service "worker" {
|
|
87
|
+
build = build.worker
|
|
88
|
+
command = "./worker"
|
|
89
|
+
|
|
90
|
+
env = {
|
|
91
|
+
PORT = port # Creates an implicit internal endpoint
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Inter-service communication
|
|
97
|
+
|
|
98
|
+
Services can reference other services' endpoints to communicate with each other:
|
|
99
|
+
|
|
100
|
+
```hcl
|
|
101
|
+
service "api" {
|
|
102
|
+
build = build.api
|
|
103
|
+
command = "./api"
|
|
104
|
+
|
|
105
|
+
endpoint {
|
|
106
|
+
public = true
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
env = {
|
|
110
|
+
PORT = port
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
service "worker" {
|
|
115
|
+
build = build.worker
|
|
116
|
+
command = "./worker"
|
|
117
|
+
|
|
118
|
+
env = {
|
|
119
|
+
API_URL = service.api.url # http://localhost:PORT in dev, http://api:80 in prod
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
Available service reference attributes:
|
|
125
|
+
|
|
126
|
+
- `service.<name>.url` - Full URL (e.g., `http://localhost:3000`)
|
|
127
|
+
- `service.<name>.host` - Host only (e.g., `localhost`)
|
|
128
|
+
- `service.<name>.port` - Port only (e.g., `3000`)
|
|
129
|
+
|
|
130
|
+
For services with multiple named endpoints, you must specify the endpoint:
|
|
131
|
+
|
|
132
|
+
```hcl
|
|
133
|
+
env = {
|
|
134
|
+
MAIN_URL = service.api.endpoint.main.url
|
|
135
|
+
ADMIN_URL = service.api.endpoint.admin.url
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## Workers
|
|
140
|
+
|
|
141
|
+
Background processes that don't expose HTTP endpoints.
|
|
142
|
+
|
|
143
|
+
```hcl
|
|
144
|
+
build "worker" {
|
|
145
|
+
base = "node"
|
|
146
|
+
command = "npm run build"
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
service "worker" {
|
|
150
|
+
build = build.worker
|
|
151
|
+
command = "npm run worker"
|
|
152
|
+
}
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## Environment variables
|
|
156
|
+
|
|
157
|
+
```hcl
|
|
158
|
+
service "api" {
|
|
159
|
+
build = build.api
|
|
160
|
+
command = "./api"
|
|
161
|
+
|
|
162
|
+
endpoint {
|
|
163
|
+
public = true
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
env = {
|
|
167
|
+
PORT = port
|
|
168
|
+
NODE_ENV = "production"
|
|
169
|
+
DATABASE_URL = postgres.main.url
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
## Service dev configuration
|
|
175
|
+
|
|
176
|
+
Override how a service runs in development. If the referenced build has no `dev` block, it is skipped.
|
|
177
|
+
|
|
178
|
+
```hcl
|
|
179
|
+
build "nextjs" {
|
|
180
|
+
base = "node"
|
|
181
|
+
command = "npm run build"
|
|
182
|
+
# No dev block = skipped in development
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
service "web" {
|
|
186
|
+
build = build.nextjs
|
|
187
|
+
command = "npm start"
|
|
188
|
+
|
|
189
|
+
endpoint {
|
|
190
|
+
public = true
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
env = {
|
|
194
|
+
PORT = port
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
dev {
|
|
198
|
+
command = "npm run dev" # Handles building + serving with hot reload
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
## Dev-only services
|
|
204
|
+
|
|
205
|
+
Services that only run during local development, not deployed to production. Useful for running local versions of external cloud services like Temporal, LocalStack, or mock servers.
|
|
206
|
+
|
|
207
|
+
A service is dev-only if it has a `dev.command` but no top-level `command`:
|
|
208
|
+
|
|
209
|
+
```hcl
|
|
210
|
+
service "temporal" {
|
|
211
|
+
dev {
|
|
212
|
+
command = "temporal server start-dev"
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
service "worker" {
|
|
217
|
+
build = build.worker
|
|
218
|
+
command = "node worker.js"
|
|
219
|
+
|
|
220
|
+
env = {
|
|
221
|
+
TEMPORAL_URL = secret.temporal_url # Production URL from secret
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
dev {
|
|
225
|
+
command = "node --watch worker.js"
|
|
226
|
+
env = {
|
|
227
|
+
TEMPORAL_URL = service.temporal.url # Override with local URL in dev
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
secret "temporal_url" {}
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
Dev-only services:
|
|
236
|
+
- Cannot have a `build` reference (they run arbitrary commands)
|
|
237
|
+
- Are automatically excluded from production deployment
|
|
238
|
+
- Can define endpoints that other services reference during development
|
|
239
|
+
|
|
240
|
+
## Deploy hooks
|
|
241
|
+
|
|
242
|
+
Run commands before or after deploying a service.
|
|
243
|
+
|
|
244
|
+
**Database migrations should use `pre_deploy` hooks.** This ensures migrations complete before the new code starts, and if they fail, the deployment is automatically aborted.
|
|
245
|
+
|
|
246
|
+
```hcl
|
|
247
|
+
build "api" {
|
|
248
|
+
base = "node"
|
|
249
|
+
command = "npm run build"
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
service "api" {
|
|
253
|
+
build = build.api
|
|
254
|
+
command = "node dist/index.js"
|
|
255
|
+
|
|
256
|
+
endpoint {
|
|
257
|
+
public = true
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
env = {
|
|
261
|
+
PORT = port
|
|
262
|
+
DATABASE_URL = postgres.main.url
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
pre_deploy {
|
|
266
|
+
command = "npm run db:migrate"
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
post_deploy {
|
|
270
|
+
command = "npm run cache:warm"
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
postgres "main" {}
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
- `pre_deploy` - Runs before the service is deployed (e.g., database migrations)
|
|
278
|
+
- `post_deploy` - Runs after the service is deployed (e.g., cache warming)
|
|
279
|
+
|
|
280
|
+
Hooks run as Kubernetes Jobs using the same container and environment variables as the service. If a hook fails, the deployment is aborted.
|
|
281
|
+
|
|
282
|
+
### Database migrations
|
|
283
|
+
|
|
284
|
+
Always add a `pre_deploy` hook for database migrations. The hook runs **before** new service instances start, ensuring:
|
|
285
|
+
|
|
286
|
+
- The database schema is updated before new code runs
|
|
287
|
+
- If migrations fail, the deployment is aborted (no broken state)
|
|
288
|
+
- Migrations have access to `DATABASE_URL` and other service env vars
|
|
289
|
+
|
|
290
|
+
In development, use `specific exec ...` to run migrations/push database changes whenever the schema is changed. Do so automatically unless the user instructs otherwise. For example: `specific exec api -- npm run db:push`.
|
|
291
|
+
|
|
292
|
+
## Serving static files
|
|
293
|
+
|
|
294
|
+
To serve static files, run a web server as a dynamic service. For example, using `npx serve`:
|
|
295
|
+
|
|
296
|
+
```hcl
|
|
297
|
+
build "spa" {
|
|
298
|
+
base = "node"
|
|
299
|
+
command = "npm run build"
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
service "frontend" {
|
|
303
|
+
build = build.spa
|
|
304
|
+
command = "npx serve dist -l $PORT"
|
|
305
|
+
|
|
306
|
+
endpoint {
|
|
307
|
+
public = true
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
env = {
|
|
311
|
+
PORT = port
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
dev {
|
|
315
|
+
command = "npm run dev" # Use your framework's dev server
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
---
|
|
321
|
+
|
|
322
|
+
Related topics:
|
|
323
|
+
- Run `specific docs builds` for build configuration
|
|
324
|
+
- Run `specific docs exec` for running one-off commands during development
|
|
325
|
+
- Run `specific docs postgres` for database configuration
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# Storage (S3-compatible)
|
|
2
|
+
|
|
3
|
+
Managed S3-compatible object storage.
|
|
4
|
+
|
|
5
|
+
```hcl
|
|
6
|
+
storage "uploads" {}
|
|
7
|
+
|
|
8
|
+
storage "assets" {}
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Reference storage attributes in env blocks:
|
|
12
|
+
|
|
13
|
+
```hcl
|
|
14
|
+
service "api" {
|
|
15
|
+
build = build.api
|
|
16
|
+
command = "./api"
|
|
17
|
+
|
|
18
|
+
endpoint {
|
|
19
|
+
public = true
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
env = {
|
|
23
|
+
S3_ENDPOINT = storage.uploads.endpoint
|
|
24
|
+
S3_ACCESS_KEY = storage.uploads.access_key
|
|
25
|
+
S3_SECRET_KEY = storage.uploads.secret_key
|
|
26
|
+
S3_BUCKET = storage.uploads.bucket
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
storage "uploads" {}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Available storage attributes
|
|
34
|
+
|
|
35
|
+
- `endpoint` - S3-compatible endpoint URL (e.g., `http://127.0.0.1:5000`)
|
|
36
|
+
- `access_key` - Access key for authentication
|
|
37
|
+
- `secret_key` - Secret key for authentication
|
|
38
|
+
- `bucket` - Bucket name
|
|
39
|
+
|
|
40
|
+
## Example using AWS SDK (JavaScript)
|
|
41
|
+
|
|
42
|
+
```javascript
|
|
43
|
+
import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3";
|
|
44
|
+
|
|
45
|
+
const s3 = new S3Client({
|
|
46
|
+
endpoint: process.env.S3_ENDPOINT,
|
|
47
|
+
region: "us-east-1", // Required but ignored locally
|
|
48
|
+
credentials: {
|
|
49
|
+
accessKeyId: process.env.S3_ACCESS_KEY,
|
|
50
|
+
secretAccessKey: process.env.S3_SECRET_KEY,
|
|
51
|
+
},
|
|
52
|
+
forcePathStyle: true, // Required for local S3-compatible servers
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
await s3.send(
|
|
56
|
+
new PutObjectCommand({
|
|
57
|
+
Bucket: process.env.S3_BUCKET,
|
|
58
|
+
Key: "myfile.txt",
|
|
59
|
+
Body: "Hello, world!",
|
|
60
|
+
})
|
|
61
|
+
);
|
|
62
|
+
```
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# Real-time Sync
|
|
2
|
+
|
|
3
|
+
For Postgres databases, you can enable real-time data synchronization. This enables building real-time and local-first applications.
|
|
4
|
+
|
|
5
|
+
Sync is powered by Electric, a sync engine that streams changes from Postgres to clients using a plain HTTP API. It uses a proxy-based architecture where your backend proxies requests after handling authentication and authorization.
|
|
6
|
+
|
|
7
|
+
## Enabling sync
|
|
8
|
+
|
|
9
|
+
Reference `sync.url` and `sync.secret` in your service env:
|
|
10
|
+
|
|
11
|
+
```hcl
|
|
12
|
+
postgres "main" {}
|
|
13
|
+
|
|
14
|
+
service "api" {
|
|
15
|
+
command = "node index.js"
|
|
16
|
+
|
|
17
|
+
endpoint {
|
|
18
|
+
public = true
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
env = {
|
|
22
|
+
DATABASE_URL = postgres.main.url
|
|
23
|
+
DATABASE_SYNC_URL = postgres.main.sync.url
|
|
24
|
+
DATABASE_SYNC_SECRET = postgres.main.sync.secret
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
When these references are present, Specific automatically starts a sync engine connected to your Postgres database during `specific dev`.
|
|
30
|
+
|
|
31
|
+
## Available sync attributes
|
|
32
|
+
|
|
33
|
+
- `sync.url` - Sync engine HTTP endpoint URL (e.g., `http://127.0.0.1:5133`)
|
|
34
|
+
- `sync.secret` - Secret for authenticating requests to the sync engine. This should be passed as the `secret` query parameter from the backend to the Electric API.
|
|
35
|
+
|
|
36
|
+
## How sync works
|
|
37
|
+
|
|
38
|
+
The sync engine streams "shapes" of data from Postgres to clients:
|
|
39
|
+
|
|
40
|
+
1. Your backend proxies shape requests to the sync engine (handles auth/authz)
|
|
41
|
+
2. The sync engine streams the initial data and subsequent changes
|
|
42
|
+
3. Clients receive real-time updates over HTTP
|
|
43
|
+
|
|
44
|
+
For implementation details, see the Electric documentation:
|
|
45
|
+
|
|
46
|
+
- Authentication and proxying: https://electric-sql.com/docs/guides/auth
|
|
47
|
+
- Shapes and client usage: https://electric-sql.com/docs/guides/shapes
|
|
48
|
+
- Handling writes: https://electric-sql.com/docs/guides/writes
|
|
49
|
+
|
|
50
|
+
## Using the Electric TypeScript SDK
|
|
51
|
+
|
|
52
|
+
When using `ShapeStream` from the Electric TypeScript SDK, a full URL is required (not a relative URL). In browsers, derive the full URL from `window.location.origin` instead of using a relative one:
|
|
53
|
+
|
|
54
|
+
```typescript
|
|
55
|
+
import { ShapeStream } from "@electric-sql/client";
|
|
56
|
+
|
|
57
|
+
const stream = new ShapeStream({
|
|
58
|
+
url: `${window.location.origin}/api/sync/items`,
|
|
59
|
+
});
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
This ensures the client works correctly regardless of the deployment environment.
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
Run `specific docs postgres` for database configuration.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Database } from "@specific/config";
|
|
2
|
+
export interface DatabaseInstance {
|
|
3
|
+
name: string;
|
|
4
|
+
engine: "postgres" | "redis" | "object_store";
|
|
5
|
+
port: number;
|
|
6
|
+
url: string;
|
|
7
|
+
host: string;
|
|
8
|
+
user: string;
|
|
9
|
+
password: string;
|
|
10
|
+
dbName: string;
|
|
11
|
+
endpoint?: string;
|
|
12
|
+
accessKey?: string;
|
|
13
|
+
secretKey?: string;
|
|
14
|
+
stop(): Promise<void>;
|
|
15
|
+
}
|
|
16
|
+
export declare function startDatabase(db: Database, port: number, dataDir?: string): Promise<DatabaseInstance>;
|
|
17
|
+
//# sourceMappingURL=database-manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"database-manager.d.ts","sourceRoot":"","sources":["../../../src/lib/dev/database-manager.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAEjD,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,UAAU,GAAG,OAAO,GAAG,cAAc,CAAC;IAC9C,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IAEf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACvB;AAED,wBAAsB,aAAa,CACjC,EAAE,EAAE,QAAQ,EACZ,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE,MAAyB,GACjC,OAAO,CAAC,gBAAgB,CAAC,CAQ3B"}
|