@tdk-landscape/tdk-cli-core 1.3.91 → 1.3.93

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.
Files changed (33) hide show
  1. package/cli/dist/backend-frameworks/mcp.js +87 -0
  2. package/cli/dist/backend-frameworks/registry.js +16 -0
  3. package/cli/dist/commands/completion.js +3 -3
  4. package/cli/dist/commands/doctor.js +8 -2
  5. package/cli/dist/commands/networks.js +2 -1
  6. package/cli/dist/commands/project.js +15 -2
  7. package/cli/dist/commands/resource.js +37 -22
  8. package/cli/dist/commands/up.js +37 -1
  9. package/cli/dist/types/index.js +11 -0
  10. package/cli/dist/utils/constants.js +4 -0
  11. package/cli/dist/utils/doctor-runtime.js +3 -2
  12. package/cli/dist/utils/doctor-wiring.js +2 -1
  13. package/cli/dist/utils/env-validator.js +118 -30
  14. package/cli/dist/utils/resource-kind.js +8 -0
  15. package/cli/dist/utils/service-manifest.js +23 -3
  16. package/cli/dist/utils/service-urls.js +4 -3
  17. package/cli/dist/utils/services.js +2 -1
  18. package/cli/dist/utils/smoke.js +225 -0
  19. package/cli/package.json +2 -1
  20. package/engine/schemas/service-schema.json +145 -27
  21. package/engine/topologies/platform/docker/auth/auth.star +66 -28
  22. package/engine/topologies/platform/docker/compose/compose.star +8 -26
  23. package/engine/topologies/platform/docker/layers/l3_builder_layers.star +3 -0
  24. package/engine/topologies/platform/docker/layers/l4_runtime_layers.star +3 -0
  25. package/engine/topologies/platform/docker/networking/api_path_constants.star +1 -57
  26. package/engine/topologies/platform/docker/secrets/env_secrets.star +72 -0
  27. package/engine/topologies/tilt/common/utils.star +0 -2
  28. package/engine/topologies/tilt/common/utils_env.star +72 -25
  29. package/engine/topologies/tilt/manifest/constants.star +2 -0
  30. package/engine/topologies/tilt/manifest/json_to_yaml.py +8 -3
  31. package/engine/topologies/tilt/manifest/loader.star +7 -0
  32. package/engine/topologies/tilt/resources/orchestrator/generators/env.star +43 -96
  33. package/package.json +1 -1
@@ -9,7 +9,10 @@
9
9
  "appType": {
10
10
  "const": "backend"
11
11
  }
12
- }
12
+ },
13
+ "required": [
14
+ "appType"
15
+ ]
13
16
  },
14
17
  "then": {
15
18
  "properties": {
@@ -29,7 +32,10 @@
29
32
  "appType": {
30
33
  "const": "frontend"
31
34
  }
32
- }
35
+ },
36
+ "required": [
37
+ "appType"
38
+ ]
33
39
  },
34
40
  "then": {
35
41
  "properties": {
@@ -46,7 +52,10 @@
46
52
  "appType": {
47
53
  "const": "worker"
48
54
  }
49
- }
55
+ },
56
+ "required": [
57
+ "appType"
58
+ ]
50
59
  },
51
60
  "then": {
52
61
  "properties": {
@@ -67,7 +76,10 @@
67
76
  "appType": {
68
77
  "const": "bring-your-own"
69
78
  }
70
- }
79
+ },
80
+ "required": [
81
+ "appType"
82
+ ]
71
83
  },
72
84
  "then": {
73
85
  "properties": {
@@ -107,7 +119,7 @@
107
119
  "type": "string"
108
120
  },
109
121
  "appType": {
110
- "description": "Resource type determining build strategy:\n• backend - Hono/Express API server\n• frontend - Vite single-page application (React, Vue, Svelte, Preact, Lit, Solid, Qwik or Vanilla TypeScript, see framework)\n• sdk - Shared library package\n• worker - Background job processor\n• migrator - Database migration runner\n• library - Shared code library\n• bring-your-own - Wrap an existing service. No app scaffold. Needs Dockerfile or --image.",
122
+ "description": "Resource type determining build strategy:\n• backend - Hono/Express API server\n• frontend - Vite single-page application (React, Vue, Svelte, Preact, Lit, Solid, Qwik or Vanilla TypeScript, see framework)\n• sdk - Shared library package\n• worker - Background job processor\n• migrator - Database migration runner\n• library - Shared code library\n• mcp - Model Context Protocol server (an HTTP service on Bun, run like a backend)\n• bring-your-own - Wrap an existing service. No app scaffold. Needs Dockerfile or --image.",
111
123
  "enum": [
112
124
  "backend",
113
125
  "frontend",
@@ -115,6 +127,7 @@
115
127
  "worker",
116
128
  "migrator",
117
129
  "library",
130
+ "mcp",
118
131
  "bring-your-own"
119
132
  ],
120
133
  "type": "string"
@@ -179,10 +192,10 @@
179
192
  "type": "object"
180
193
  },
181
194
  "secrets": {
182
- "description": "Secret references resolved via Infisical. NO secrets in params/envVars - security violation.",
195
+ "description": "Names of secrets the resource needs. By default each is read from the project .env (gitignored) and injected into the container; with TDK_SECRET_PROVIDER=infisical they are left to Infisical. NO secret values in params/envVars or anywhere in service.json.",
183
196
  "properties": {
184
197
  "required": {
185
- "description": "Required secret names. Resource CRASHES if any are missing.",
198
+ "description": "Required secret names. The resource does not start if any is missing from the project .env.",
186
199
  "items": {
187
200
  "type": "string",
188
201
  "pattern": "^[A-Z_][A-Z0-9_]*$"
@@ -191,7 +204,7 @@
191
204
  "uniqueItems": true
192
205
  },
193
206
  "optional": {
194
- "description": "Optional secret names. Resource starts but warns if missing.",
207
+ "description": "Optional secret names. The resource starts with the variable empty when it is missing from the project .env.",
195
208
  "items": {
196
209
  "type": "string",
197
210
  "pattern": "^[A-Z_][A-Z0-9_]*$"
@@ -206,7 +219,7 @@
206
219
  "type": "object"
207
220
  },
208
221
  "featuresEnabled": {
209
- "description": "Platform features:\n• nats - NATS JetStream eventing\n• prisma - DB ORM & migrations\n• redis - Caching layer\n• infisical - Secrets management\n• vitest - Test framework\n• traefik - Reverse proxy routing\n• websocket - WS support\n• graphql - GraphQL server\n• grpc - gRPC protocol",
222
+ "description": "Platform features:\n• nats - NATS JetStream eventing\n• prisma - DB ORM & migrations\n• redis - Caching layer\n• infisical - Secrets management\n• vitest - Test framework\n• traefik - Reverse proxy routing\n• websocket - WS support\n• graphql - GraphQL server\n• grpc - gRPC protocol\n• vite, tsconfig, dockerfile, nginx, ... - generator features that `tdk resource` writes into new files",
210
223
  "items": {
211
224
  "enum": [
212
225
  "nats",
@@ -217,7 +230,21 @@
217
230
  "traefik",
218
231
  "websocket",
219
232
  "graphql",
220
- "grpc"
233
+ "grpc",
234
+ "api-client",
235
+ "env-config",
236
+ "api-index",
237
+ "vite",
238
+ "tsconfig",
239
+ "dockerfile",
240
+ "npmrc",
241
+ "bunfig",
242
+ "nginx",
243
+ "env",
244
+ "playwright",
245
+ "agents-md",
246
+ "ddd",
247
+ "sablier"
221
248
  ],
222
249
  "type": "string"
223
250
  },
@@ -229,19 +256,7 @@
229
256
  "examples": [
230
257
  "react",
231
258
  "vue",
232
- "svelte",
233
- "preact",
234
- "lit",
235
- "solid",
236
- "qwik",
237
- "vanilla",
238
- "hono",
239
- "express",
240
- "elysia",
241
- "fastify",
242
- "nestjs",
243
- "koa",
244
- "h3"
259
+ "solid"
245
260
  ],
246
261
  "pattern": "^[a-z][a-z0-9-]*$",
247
262
  "type": "string"
@@ -251,8 +266,7 @@
251
266
  "examples": [
252
267
  "bun",
253
268
  "python",
254
- "go",
255
- "rust"
269
+ "go"
256
270
  ],
257
271
  "pattern": "^[a-z][a-z0-9-]*$",
258
272
  "type": "string"
@@ -360,6 +374,77 @@
360
374
  },
361
375
  "type": "array"
362
376
  },
377
+ "smoke": {
378
+ "description": "Post-start check run by `tdk up` through the public proxy URL (not inside the container): write a record, read it back. `tdk up` fails if a step misses. Absent means no smoke check. Workers and resources with exposeViaProxy false are skipped.",
379
+ "type": "object",
380
+ "required": [
381
+ "via",
382
+ "steps"
383
+ ],
384
+ "additionalProperties": false,
385
+ "properties": {
386
+ "via": {
387
+ "const": "proxy",
388
+ "description": "Only `proxy` is supported: the request goes to the URL `tdk up` prints."
389
+ },
390
+ "timeoutSeconds": {
391
+ "type": "number",
392
+ "exclusiveMinimum": 0,
393
+ "default": 60,
394
+ "description": "How long to wait for the route to come up. Connection errors and 404/502/503/504 are retried until then; any other wrong answer fails at once."
395
+ },
396
+ "steps": {
397
+ "type": "array",
398
+ "minItems": 1,
399
+ "items": {
400
+ "type": "object",
401
+ "required": [
402
+ "path"
403
+ ],
404
+ "additionalProperties": false,
405
+ "properties": {
406
+ "name": {
407
+ "type": "string",
408
+ "description": "Optional display name for this check."
409
+ },
410
+ "method": {
411
+ "type": "string",
412
+ "default": "GET",
413
+ "description": "HTTP method. Defaults to GET."
414
+ },
415
+ "path": {
416
+ "type": "string",
417
+ "pattern": "^/(?!/)",
418
+ "description": "Appended to the service's public base URL. `{{name}}` is replaced by a value saved by an earlier step."
419
+ },
420
+ "body": {
421
+ "description": "JSON request body."
422
+ },
423
+ "expect": {
424
+ "type": "integer",
425
+ "minimum": 100,
426
+ "maximum": 599,
427
+ "default": 200,
428
+ "description": "Expected HTTP status code, from 100 through 599."
429
+ },
430
+ "bodyContains": {
431
+ "type": "string",
432
+ "description": "Optional text that must occur in the response body."
433
+ },
434
+ "save": {
435
+ "type": "object",
436
+ "additionalProperties": {
437
+ "type": "string",
438
+ "pattern": "^\\$\\."
439
+ },
440
+ "description": "Saves response fields for later steps, for example {\"id\": \"$.id\"}."
441
+ }
442
+ }
443
+ },
444
+ "description": "Non-empty ordered list of HTTP checks to run after the resource starts."
445
+ }
446
+ }
447
+ },
363
448
  "traefik": {
364
449
  "description": "Custom Traefik routing. Auto-generated if not specified.",
365
450
  "properties": {
@@ -377,14 +462,47 @@
377
462
  }
378
463
  },
379
464
  "type": "object"
465
+ },
466
+ "type": {
467
+ "description": "Legacy alias for appType, still read from older service.json files. Use appType in new files.",
468
+ "enum": [
469
+ "backend",
470
+ "frontend",
471
+ "sdk",
472
+ "worker",
473
+ "migrator",
474
+ "library",
475
+ "mcp",
476
+ "bring-your-own"
477
+ ],
478
+ "type": "string"
479
+ },
480
+ "enabled": {
481
+ "description": "Whether this resource is enabled in its project configuration.",
482
+ "type": "boolean"
483
+ },
484
+ "apiPath": {
485
+ "description": "API path exposed by this resource, when applicable.",
486
+ "type": "string"
380
487
  }
381
488
  },
382
489
  "required": [
383
490
  "appName",
384
- "appType",
385
491
  "stack",
386
492
  "schemaVersion"
387
493
  ],
388
494
  "title": "TDK Landscape Resource Configuration",
389
- "type": "object"
495
+ "type": "object",
496
+ "anyOf": [
497
+ {
498
+ "required": [
499
+ "appType"
500
+ ]
501
+ },
502
+ {
503
+ "required": [
504
+ "type"
505
+ ]
506
+ }
507
+ ]
390
508
  }
@@ -12,38 +12,80 @@
12
12
  # them as the auth_config dict.
13
13
  # =============================================================================
14
14
 
15
- # Default values for auth configuration
15
+ # Default values for auth configuration.
16
+ #
17
+ # There is deliberately no default JWT secret: a secret that is the same in every project is not a secret. The JWT secret is
18
+ # generated once per project by the CLI into the project `.env` (JWT_SECRET) and Compose reads it from there.
16
19
  DEFAULT_AUTH_MODE = 'local-jwt'
17
- DEFAULT_JWT_SECRET = 'local-development-secret-min-32-chars-long'
18
20
  DEFAULT_IDENTITY_RESOURCE_URL = 'http://identity-service:3000'
19
21
 
22
+ # Shown by Compose when the project `.env` has no JWT_SECRET.
23
+ JWT_SECRET_MISSING_MESSAGE = 'JWT_SECRET is not set. Run `tdk up` (it adds a generated one to the project .env) or add JWT_SECRET to .env'
24
+
25
+
26
+ def _setting(auth_config, key, default):
27
+ """A configured value, or the default when the key is missing OR present with a None value.
28
+
29
+ Callers build the config dict with every key present and None for anything unset, and `dict.get(key, default)` returns that
30
+ None instead of the default, which used to turn into the literal text `AUTH_MODE=None` in generated files.
31
+ """
32
+ if not auth_config:
33
+ return default
34
+ value = auth_config.get(key)
35
+ if value == None or value == '':
36
+ return default
37
+ return value
20
38
 
21
- def get_auth_mode(auth_config):
22
- """Get auth mode from auth config with default fallback.
39
+
40
+ def _first_set(*values):
41
+ """The first value that is not None or an empty string, else None."""
42
+ for value in values:
43
+ if value != None and value != '':
44
+ return value
45
+ return None
46
+
47
+
48
+ def auth_config_from_manifest(manifest):
49
+ """Build the auth configuration both the env file and the Compose entry use, from one place.
50
+
51
+ The two writers used to build this separately and drifted: the env file read `identityServiceUrl` while Compose read
52
+ `envVars.IDENTITY_RESOURCE_URL`, so setting one field changed only one output.
53
+
54
+ - authMode: the manifest's `authMode`, else `identity-service` when `dependsOn` contains `identity`, else unset (local-jwt).
55
+ - identityServiceUrl: the manifest's `identityServiceUrl`. The deprecated `params` / `envVars` entry named
56
+ `IDENTITY_RESOURCE_URL` is still honoured when the field is absent, so an existing manifest keeps working.
23
57
 
24
58
  Args:
25
- auth_config: Auth configuration dictionary (may be None or empty)
59
+ manifest: The service manifest dict (may be None)
26
60
 
27
61
  Returns:
28
- str: Auth mode ('local-jwt' or 'identity-service')
62
+ dict: {'authMode': str or None, 'identityServiceUrl': str or None}; None means "use the default"
29
63
  """
30
- if not auth_config:
31
- return DEFAULT_AUTH_MODE
32
- return auth_config.get('authMode', DEFAULT_AUTH_MODE)
64
+ if not manifest:
65
+ return {'authMode': None, 'identityServiceUrl': None}
66
+
67
+ auth_mode = _first_set(
68
+ manifest.get('authMode'),
69
+ 'identity-service' if 'identity' in (manifest.get('dependsOn') or []) else None,
70
+ )
71
+ identity_url = _first_set(
72
+ manifest.get('identityServiceUrl'),
73
+ (manifest.get('params') or {}).get('IDENTITY_RESOURCE_URL'),
74
+ (manifest.get('envVars') or {}).get('IDENTITY_RESOURCE_URL'),
75
+ )
76
+ return {'authMode': auth_mode, 'identityServiceUrl': identity_url}
33
77
 
34
78
 
35
- def get_jwt_secret(auth_config):
36
- """Get JWT secret from auth config with default fallback.
79
+ def get_auth_mode(auth_config):
80
+ """Get auth mode from auth config with default fallback.
37
81
 
38
82
  Args:
39
83
  auth_config: Auth configuration dictionary (may be None or empty)
40
84
 
41
85
  Returns:
42
- str: JWT secret for local-jwt mode
86
+ str: Auth mode ('local-jwt' or 'identity-service')
43
87
  """
44
- if not auth_config:
45
- return DEFAULT_JWT_SECRET
46
- return auth_config.get('jwtSecret', DEFAULT_JWT_SECRET)
88
+ return _setting(auth_config, 'authMode', DEFAULT_AUTH_MODE)
47
89
 
48
90
 
49
91
  def get_identity_resource_url(auth_config):
@@ -55,9 +97,7 @@ def get_identity_resource_url(auth_config):
55
97
  Returns:
56
98
  str: URL for identity service
57
99
  """
58
- if not auth_config:
59
- return DEFAULT_IDENTITY_RESOURCE_URL
60
- return auth_config.get('identityServiceUrl', DEFAULT_IDENTITY_RESOURCE_URL)
100
+ return _setting(auth_config, 'identityServiceUrl', DEFAULT_IDENTITY_RESOURCE_URL)
61
101
 
62
102
 
63
103
  def get_auth_config(auth_config):
@@ -67,11 +107,10 @@ def get_auth_config(auth_config):
67
107
  auth_config: Auth configuration dictionary (may be None or empty)
68
108
 
69
109
  Returns:
70
- struct: Auth configuration with mode, jwt_secret, and identity_resource_url
110
+ struct: Auth configuration with mode and identity_resource_url (the JWT secret lives in the project .env, not here)
71
111
  """
72
112
  return struct(
73
113
  mode = get_auth_mode(auth_config),
74
- jwt_secret = get_jwt_secret(auth_config),
75
114
  identity_resource_url = get_identity_resource_url(auth_config),
76
115
  )
77
116
 
@@ -113,12 +152,12 @@ def generate_docker_compose_auth_env(auth_config):
113
152
  str: Formatted auth environment variables for docker-compose
114
153
  """
115
154
  auth_mode = get_auth_mode(auth_config)
116
- jwt_secret = get_jwt_secret(auth_config)
117
155
  identity_resource_url = get_identity_resource_url(auth_config)
118
156
 
119
157
  if auth_mode == 'local-jwt':
158
+ # Required from the project .env (see JWT_SECRET_MISSING_MESSAGE); there is no shared default.
120
159
  return """ - AUTH_MODE=local-jwt
121
- - JWT_SECRET=${JWT_SECRET:-%(jwt_secret)s}""" % {'jwt_secret': jwt_secret}
160
+ - JWT_SECRET=${JWT_SECRET:?%(message)s}""" % {'message': JWT_SECRET_MISSING_MESSAGE}
122
161
  else:
123
162
  return """ - AUTH_MODE=identity-service
124
163
  - IDENTITY_RESOURCE_URL=${IDENTITY_RESOURCE_URL:-%(identity_resource_url)s}""" % {'identity_resource_url': identity_resource_url}
@@ -128,7 +167,9 @@ def generate_env_file_auth_section(auth_config):
128
167
  """Generate .env file auth section content.
129
168
 
130
169
  This function returns the formatted authentication configuration section
131
- for .env.backend.autogenerated files.
170
+ for .env.backend.autogenerated files. These files are generated, checked by
171
+ `tdk config verify` and never carry secrets, so there is no JWT secret here:
172
+ in local-jwt mode Compose takes JWT_SECRET from the project .env.
132
173
 
133
174
  Args:
134
175
  auth_config: Auth configuration dictionary (may be None or empty)
@@ -137,7 +178,6 @@ def generate_env_file_auth_section(auth_config):
137
178
  str: Formatted auth section for .env files
138
179
  """
139
180
  auth_mode = get_auth_mode(auth_config)
140
- jwt_secret = get_jwt_secret(auth_config)
141
181
  identity_resource_url = get_identity_resource_url(auth_config)
142
182
 
143
183
  content = """
@@ -146,9 +186,7 @@ def generate_env_file_auth_section(auth_config):
146
186
  AUTH_MODE={auth_mode}
147
187
  """.format(auth_mode=auth_mode)
148
188
 
149
- if auth_mode == 'local-jwt':
150
- content += "JWT_SECRET={jwt_secret}\n".format(jwt_secret=jwt_secret)
151
- else:
189
+ if auth_mode != 'local-jwt':
152
190
  content += "IDENTITY_RESOURCE_URL={identity_resource_url}\n".format(identity_resource_url=identity_resource_url)
153
191
 
154
192
  return content
@@ -156,8 +194,8 @@ AUTH_MODE={auth_mode}
156
194
 
157
195
  # Export all functions as a struct for easy importing
158
196
  AuthConfig = struct(
197
+ from_manifest = auth_config_from_manifest,
159
198
  get_auth_mode = get_auth_mode,
160
- get_jwt_secret = get_jwt_secret,
161
199
  get_identity_resource_url = get_identity_resource_url,
162
200
  get_auth_config = get_auth_config,
163
201
  is_local_jwt_mode = is_local_jwt_mode,
@@ -20,6 +20,7 @@ load("./compose_helpers.star", "with_runtime_image_tag", "compose_build_config")
20
20
  load("../networking/traefik.star", "get_frontend_traefik_labels", "get_backend_traefik_labels")
21
21
  load("../networking/api_path_constants.star", "get_api_path_for_domain", "get_frontend_base_path")
22
22
  load("../secrets/infisical.star", "get_infisical_environment_vars")
23
+ load("../secrets/env_secrets.star", "get_secret_environment_lines")
23
24
  load("../auth/auth.star", "AuthConfig")
24
25
 
25
26
  load("../constants.star", "PlatformDockerConstants")
@@ -189,32 +190,12 @@ def _generate_single_backend_entry(resource_path, resource_name, res, manifest,
189
190
  nats_queue = nats_cfg.get('queueGroup', stack + '_backend_svc')
190
191
  nats_url = PlatformDockerConstants.NATS_URL
191
192
 
192
- # Auth configuration - use centralized auth utilities
193
- # Extract auth config from manifest to decouple auth.star from manifest structure
194
- # Auto-detect identity-service mode if 'identity' is in dependsOn
195
- internal_deps = manifest.get('dependsOn', []) if manifest else []
196
- has_identity_dep = 'identity' in internal_deps
197
-
198
- # If manifest has authMode, use it; otherwise auto-detect based on identity dependency
199
- manifest_auth_mode = manifest.get('authMode') if manifest else None
200
- if manifest_auth_mode:
201
- auth_mode = manifest_auth_mode
202
- elif has_identity_dep:
203
- auth_mode = 'identity-service'
204
- else:
205
- auth_mode = None
206
-
207
- auth_config = {
208
- 'authMode': auth_mode,
209
- 'jwtSecret': manifest.get('jwtSecret') if manifest else None,
210
- 'identityServiceUrl': manifest.get('envVars', {}).get('IDENTITY_RESOURCE_URL') if manifest else None,
211
- }
212
-
213
- # If we auto-detected identity-service mode but no URL is set, use default
214
- if auth_mode == 'identity-service' and not auth_config['identityServiceUrl']:
215
- auth_config['identityServiceUrl'] = AuthConfig.get_identity_resource_url(None)
193
+ # Auth configuration: one shared builder keeps this identical to the generated env file (see AuthConfig.from_manifest).
194
+ auth_config = AuthConfig.from_manifest(manifest)
216
195
 
217
196
  auth_env = AuthConfig.generate_docker_compose_auth_env(auth_config)
197
+ # secrets.required / secrets.optional, read from the project .env by Compose (empty with TDK_SECRET_PROVIDER=infisical)
198
+ secrets_env = get_secret_environment_lines(manifest, res_name)
218
199
 
219
200
  # Use .autogenerated/Dockerfile.app.autogenerated by default for build config (primary only)
220
201
  if manifest and manifest.get('appType') == 'bring-your-own':
@@ -240,7 +221,7 @@ def _generate_single_backend_entry(resource_path, resource_name, res, manifest,
240
221
 
241
222
  # REMOVED: External port exposure - all services accessed via Traefik only
242
223
  # Services are available at {project}.localhost/api/v1/{service}
243
- # Internal port 3000 is accessible within Docker network for service-to-service communication
224
+ # Inside the Docker network a service listens on its own port (manifest.port, passed to it as PORT), for service-to-service calls
244
225
  ports_section = "" # No external ports - Traefik-only access
245
226
 
246
227
  # Determine service name based on instance
@@ -325,7 +306,7 @@ def _generate_single_backend_entry(resource_path, resource_name, res, manifest,
325
306
  # to skip Prisma auto-migration at container boot (e.g. while diagnosing a
326
307
  # migration/CLI issue) without touching the generator or any service code.
327
308
  - AUTO_MIGRATE=${{AUTO_MIGRATE:-true}}
328
- {auth_env}
309
+ {auth_env}{secrets_env}
329
310
  labels:
330
311
  {traefik_labels}
331
312
  networks:
@@ -373,6 +354,7 @@ def _generate_single_backend_entry(resource_path, resource_name, res, manifest,
373
354
  healthcheck_section=healthcheck_section,
374
355
  infisical_env=infisical_env,
375
356
  auth_env=auth_env,
357
+ secrets_env=secrets_env,
376
358
  network_traefik_public=PlatformDockerConstants.NETWORK_TRAEFIK_PUBLIC,
377
359
  network_backend=PlatformDockerConstants.NETWORK_BACKEND,
378
360
  network_database=PlatformDockerConstants.NETWORK_DATABASE,
@@ -28,6 +28,9 @@ load('./prisma/prisma_build.star',
28
28
  'prisma_normalize_output',
29
29
  'L3_generate_migration_engine',
30
30
  )
31
+ # Infisical is opt-in: use_infisical defaults to False and nothing in the free engine passes True, so the generator behind this
32
+ # (tilt/generators/infisical/secrets_generator.star) is not on the default path. It is an older contract with fixed secret names and
33
+ # ports. Do not wire `secrets.required` through it; the default provider is platform/docker/secrets/env_secrets.star.
31
34
  load('./infisical/infisical_docker.star', 'InfisicalDocker')
32
35
 
33
36
  # Load project name for dynamic golden image naming
@@ -29,6 +29,9 @@ load('./prisma/prisma_runtime.star',
29
29
 
30
30
  load('./bun_helpers.star', 'bun_hoisted_packages_symlink_fix')
31
31
  load('../config/healthcheck.star', 'DOCKER_HEALTHCHECK', 'dockerfile_healthcheck_flags')
32
+ # Infisical is opt-in: use_infisical defaults to False and nothing in the free engine passes True, so the generator behind this
33
+ # (tilt/generators/infisical/secrets_generator.star) is not on the default path. It is an older contract with fixed secret names and
34
+ # ports. Do not wire `secrets.required` through it; the default provider is platform/docker/secrets/env_secrets.star.
32
35
  load('./infisical/infisical_docker.star', 'InfisicalDocker')
33
36
 
34
37
 
@@ -33,21 +33,6 @@ def generate_api_path(stack, app_name):
33
33
  # Examples: /api/user-management, /api/order-management
34
34
  return "/api/" + stack + "-management"
35
35
 
36
- # =============================================================================
37
- # SERVICE STACK TO API PATH MAPPING (Dynamic)
38
- # =============================================================================
39
- # This is populated at runtime from discovered manifests
40
- # No hardcoded resource names
41
-
42
- RESOURCE_STACK_TO_API_PATH = {}
43
-
44
- # =============================================================================
45
- # API PATH TO SERVICE MAPPING (Dynamic)
46
- # =============================================================================
47
-
48
- API_PATH_TO_RESOURCE_STACK = {}
49
- API_PATH_TO_RESOURCE_DOMAIN = API_PATH_TO_RESOURCE_STACK
50
-
51
36
  # =============================================================================
52
37
  # HELPER FUNCTIONS
53
38
  # =============================================================================
@@ -110,7 +95,7 @@ def get_api_path_for_stack(stack, manifest=None):
110
95
  return manifest.get("apiPath")
111
96
 
112
97
  # NEW: Use /api/{stack}-management pattern instead of /api/v1/{pluralized}
113
- return RESOURCE_STACK_TO_API_PATH.get(stack, "/api/" + stack + "-management")
98
+ return "/api/" + stack + "-management"
114
99
 
115
100
  # Backwards compatibility alias
116
101
  get_api_path_for_domain = get_api_path_for_stack
@@ -176,20 +161,6 @@ def get_api_path_for_resource(resource_name):
176
161
  # Backwards compatibility alias
177
162
  get_api_path_for_service = get_api_path_for_resource
178
163
 
179
- def get_stack_for_api_path(api_path):
180
- """Returns the service stack for an API path (reverse lookup).
181
-
182
- Args:
183
- api_path: Full API path
184
-
185
- Returns:
186
- Service stack string
187
- """
188
- return API_PATH_TO_RESOURCE_STACK.get(api_path, "")
189
-
190
- # Backwards compatibility alias
191
- get_domain_for_api_path = get_stack_for_api_path
192
-
193
164
  # Load project name for dynamic localhost domain
194
165
  # Use TDK_PROJECT_ROOT env var set by Tilt, fallback to current directory
195
166
  def _load_project_localhost():
@@ -231,30 +202,3 @@ def build_health_endpoint(api_path, health_path="/health"):
231
202
  Full health endpoint path
232
203
  """
233
204
  return api_path + health_path
234
-
235
- def get_all_api_paths():
236
- """Returns a list of all defined API paths from discovered services.
237
-
238
- Returns:
239
- List of API path strings
240
- """
241
- return RESOURCE_DOMAIN_TO_API_PATH.values()
242
-
243
- def is_valid_api_path(api_path):
244
- """Checks if an API path is valid/defined.
245
-
246
- Args:
247
- api_path: API path to check
248
-
249
- Returns:
250
- True if valid, False otherwise
251
- """
252
- return api_path in API_PATH_TO_RESOURCE_DOMAIN
253
-
254
- # =============================================================================
255
- # LEGACY COMPATIBILITY (Deprecated - for migration only)
256
- # =============================================================================
257
- # All legacy paths removed - use dynamic path generation from manifests
258
-
259
- LEGACY_DOMAIN_TO_API_PATH = {}
260
- RESOURCE_DOMAIN_TO_API_PATH = LEGACY_DOMAIN_TO_API_PATH