@withmata/blueprints 0.7.0 → 3.0.0

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.
@@ -76,7 +76,7 @@ Regardless of whether project context exists, explore the actual project to unde
76
76
  ### 3f. Environment & Validation
77
77
 
78
78
  - How are env vars handled? t3-env with client/server split, single env.ts, raw `process.env`, dotenv only?
79
- - Is there `.env.example` with documentation?
79
+ - Are the Zod schema files well-commented as the variable registry? (No `.env.example` values come from Infisical or the platform.)
80
80
  - Is there build-time validation (env imported in `next.config.ts`)?
81
81
 
82
82
  ### 3g. Code Quality
@@ -172,10 +172,10 @@ For each installed blueprint with updates, show detailed information. Use plain
172
172
  │ File: src/env/server.ts
173
173
 
174
174
  │ • Clearer documentation for teammates
175
- │ The .env.example file now has section headers and
176
- explains what each variable is for, with example
177
- values for local development and production.
178
- │ File: .env.example
175
+ │ The env schema files now document each variable
176
+ (purpose, local/production hints) the schema is
177
+ the registry; values come from Infisical.
178
+ │ File: src/env/server.ts
179
179
 
180
180
  │ To apply: /scaffold-env
181
181
  └─────────────────────────────────────────────────────
@@ -316,7 +316,7 @@ What was done
316
316
  Blueprint upgrades:
317
317
  ✓ foundation — consolidated maintenance rules,
318
318
  cleaned up legacy patterns
319
- ✓ env-t3 — updated server.ts, .env.example
319
+ ✓ env-t3 — updated server.ts schema (the registry)
320
320
  ✓ tailwind-v4 — updated shared-styles.css, added
321
321
  tailwind-scrollbar-hide
322
322
 
@@ -21,6 +21,7 @@ user-invocable: false
21
21
  | Environment Variables | Feature | T3 Env + Zod: validated, type-safe env vars for Next.js (client/server split) and backend apps. | None | `/scaffold-env` |
22
22
  | Tailwind v4 Design System | Feature | Full Tailwind v4 upgrade: shadcn tokens, shadows, easing, animations, sidebar/chart tokens, dark mode. Auto-detects `/design-system` output. | Foundation (recommended) | `/scaffold-tailwind` |
23
23
  | UI Shared Components | Feature | 31 base components (Base UI + Phosphor): compound patterns, form system, app shell, data display. Auto-installs Tailwind. | Tailwind v4 (auto-installed) | `/scaffold-ui` |
24
+ | Secrets (Infisical) | Feature | Centralized secret management: /shared domain folders with environment-aware references, placeholder injection, and `infisical run` wiring. | Environment Variables (env-t3) | `/scaffold-infisical` |
24
25
 
25
26
  ## Project Context
26
27
 
@@ -50,6 +51,8 @@ Suggest a blueprint when you observe these signals. Be helpful, not pushy — me
50
51
 
51
52
  **`/scaffold-ui`** — User mentions shared components, UI package, component library, shadcn in monorepo, or needs to share components across apps. Recommend `/scaffold-tailwind` first if not installed.
52
53
 
54
+ **`/scaffold-infisical`** — User mentions Infisical, secret management, shared secrets across apps, secret references, rotating keys in one place, or has the same secret duplicated across multiple `.env`/Infisical folders. Requires `/scaffold-env` first (reads the T3 schemas).
55
+
53
56
  **`/audit`** — User asks "what should I do next?", wants to understand their project's state, or has hand-rolled infrastructure that a blueprint could improve.
54
57
 
55
58
  ## Lifecycle & Dependencies
@@ -64,6 +67,7 @@ Discovery → Foundation → Features is the recommended flow, but blueprints ca
64
67
  - **Need marketing pages:** Run `/copywrite-landing` for landing pages, `/copywrite` for other pages (pricing, about, features)
65
68
  - **Dependencies:** `/scaffold-auth` works best after `/scaffold-db` (auth builds on db patterns). `/scaffold-ui` auto-installs `/scaffold-tailwind`. `/scaffold-tailwind` auto-detects design system tokens.
66
69
  - **Environment setup:** `/scaffold-env` is recommended for all projects — it's standalone
70
+ - **Secret management:** after `/scaffold-env`, run `/scaffold-infisical` to move secret delivery into Infisical with a shared single-source-of-truth structure (env-aware references). Best for multi-app/multi-environment projects
67
71
 
68
72
  ## Important
69
73
 
@@ -54,7 +54,6 @@ If `env-t3` is found in `.project-context.md` under `## Installed Blueprints`, s
54
54
 
55
55
  Updated templates:
56
56
  ☐ src/env/server.ts — updated Zod patterns
57
- ☐ .env.example — new documentation sections
58
57
 
59
58
  New files available:
60
59
  ☐ apis/server/src/env.ts — backend env validation (not set up previously)
@@ -100,7 +99,6 @@ Find all applications that need env setup:
100
99
  For each app found:
101
100
  - Does `env.ts` already exist? (Foundation blueprint creates a basic one)
102
101
  - Does `src/env/` directory exist? (Already has the split pattern)
103
- - Does `.env.example` exist?
104
102
  - Does `next.config.ts` already import env files?
105
103
  - Are `@t3-oss/env-nextjs` or `@t3-oss/env-core` already installed?
106
104
 
@@ -144,9 +142,8 @@ For each Next.js app:
144
142
  3. Update `next.config.ts` — add imports at the top
145
143
 
146
144
  **For all Next.js apps:**
147
- 4. Create `.env.example` from template — replace `{{APP_NAME}}`
148
- 5. Create `.env.local` (empty file)
149
- 6. If other blueprints are installed, merge their env vars into the appropriate schema
145
+ 4. Create `.env.local` (empty file)
146
+ 5. If other blueprints are installed, merge their env vars into the appropriate schema
150
147
 
151
148
  ### 4b. Backend Apps
152
149
 
@@ -157,9 +154,8 @@ For each backend app:
157
154
  ```typescript
158
155
  import { env } from "./env.js";
159
156
  ```
160
- 3. Create `.env.example` from template — replace `{{APP_NAME}}`
161
- 4. Create `.env.local` (empty file)
162
- 5. If the app has hardcoded `process.env.PORT` or similar, refactor to use `env.PORT`
157
+ 3. Create `.env.local` (empty file)
158
+ 4. If the app has hardcoded `process.env.PORT` or similar, refactor to use `env.PORT`
163
159
 
164
160
  ### 4c. Dependency Installation
165
161
 
@@ -209,11 +205,11 @@ Then append:
209
205
 
210
206
  ```markdown
211
207
  ### env-t3 maintenance
212
- - After adding a new env var: add it to the env schema file (client.ts or server.ts), `.env.example`, and `.env.local`
208
+ - The Zod schema files (client.ts/server.ts/env.ts) are the registry of env vars keep them well-commented. There is no .env.example; values come from Infisical or the platform.
209
+ - After adding a new env var: add it to the env schema file (client.ts or server.ts) and, if used locally, .env.local
213
210
  - NEXT_PUBLIC_* vars go in env/client.ts with explicit runtimeEnv mapping; server vars go in env/server.ts
214
- - After removing an env var: remove from schema, .env.example, and .env.local
211
+ - After removing an env var: remove from the schema and .env.local
215
212
  - Never access process.env directly — always use the typed env objects (clientEnv, serverEnv, or env)
216
- - Keep .env.example documented with section headers and local/production hints
217
213
  ```
218
214
 
219
215
  If a `### env-t3 maintenance` section already exists in the file, skip — do not duplicate.
@@ -253,9 +249,10 @@ import { env } from "./env.js";
253
249
 
254
250
  ### Next Steps
255
251
 
256
- - Fill in `.env.local` with your actual values (use `.env.example` as reference)
257
- - As you add features, add their env vars to the schema files and `.env.example`
252
+ - Fill in `.env.local` with your actual values (the env schema files document what's needed), or run via `infisical run` once `/scaffold-infisical` is set up
253
+ - As you add features, add their env vars to the schema files (the registry)
258
254
  - Run `bun dev` to verify env validation passes
255
+ - For multi-app/multi-environment projects, run `/scaffold-infisical` next to centralize secret delivery (a /shared single source of truth with environment-aware references) instead of per-app `.env` files
259
256
 
260
257
  ## Important Notes
261
258
 
@@ -205,9 +205,8 @@ Follow the `env-t3` blueprint's integration steps:
205
205
  import "./src/env/server";
206
206
  import "./src/env/client";
207
207
  ```
208
- 4. Create `apps/web/.env.example` from env-t3 template — replace `{{APP_NAME}}`
209
- 5. Create `apps/web/.env.local` (empty)
210
- 6. Ensure `@t3-oss/env-nextjs` and `zod` are in `apps/web/package.json` dependencies
208
+ 4. Create `apps/web/.env.local` (empty)
209
+ 5. Ensure `@t3-oss/env-nextjs` and `zod` are in `apps/web/package.json` dependencies
211
210
 
212
211
  ### 5.5b. Frontend UI Setup (if opted in)
213
212
 
@@ -318,11 +317,11 @@ Then append sections for each blueprint applied in this run. Foundation always a
318
317
  - Keep `turbo.json` tasks with `cache: false` for any task with side effects (migrations, code generation)
319
318
 
320
319
  ### env-t3 maintenance
321
- - After adding a new env var: add it to the env schema file (client.ts or server.ts), `.env.example`, and `.env.local`
320
+ - The Zod schema files (client.ts/server.ts/env.ts) are the registry of env vars keep them well-commented. There is no .env.example; values come from Infisical or the platform.
321
+ - After adding a new env var: add it to the env schema file (client.ts or server.ts) and, if used locally, .env.local
322
322
  - NEXT_PUBLIC_* vars go in env/client.ts with explicit runtimeEnv mapping; server vars go in env/server.ts
323
- - After removing an env var: remove from schema, .env.example, and .env.local
323
+ - After removing an env var: remove from the schema and .env.local
324
324
  - Never access process.env directly — always use the typed env objects (clientEnv, serverEnv, or env)
325
- - Keep .env.example documented with section headers and local/production hints
326
325
  ```
327
326
 
328
327
  If frontend UI was applied, also append:
@@ -0,0 +1,173 @@
1
+ ---
2
+ name: scaffold-infisical
3
+ description: Scaffold Infisical secret management — a /shared domain structure with environment-aware references, placeholder injection, and infisical run wiring — driven by the project's T3 env schemas
4
+ ---
5
+
6
+ # Scaffold Infisical Secrets Blueprint
7
+
8
+ Scaffold the `secrets-infisical` blueprint into the current project. Read the full blueprint first, then adapt it to this project's stack. This blueprint **depends on `env-t3`** — it reads the T3 Zod schemas to learn which variables exist, then builds the Infisical structure around them.
9
+
10
+ ## Step 1: Read the Blueprint
11
+
12
+ Read the full BLUEPRINT.md and all template files:
13
+
14
+ ```
15
+ ~/.withmata/blueprints/blueprints/features/secrets-infisical/BLUEPRINT.md
16
+ ~/.withmata/blueprints/blueprints/features/secrets-infisical/files/**/*
17
+ ```
18
+
19
+ If not found at these paths, run `npx @withmata/blueprints` to install.
20
+
21
+ ## Step 1.5: Read Project Context
22
+
23
+ Check if `.project-context.md` exists in the target project root:
24
+
25
+ - **If it exists**: read it to understand:
26
+ - Confirm `env-t3` is installed. If NOT, **stop** and tell the user to run `/scaffold-env` first (hard dependency — the T3 schemas are the source of truth for the variable list).
27
+ - Note `db-drizzle-postgres` / `auth-better-auth` — they define the natural domains (databases, auth).
28
+ - npm scope, workspace layout, project type.
29
+ - If `secrets-infisical` is already listed → switch to upgrade mode (Step 1.6).
30
+
31
+ - **If it does not exist**: env-t3 cannot be confirmed installed. Ask the user whether the project has T3 env schemas; if not, point them to `/scaffold-env` first.
32
+
33
+ ## Step 1.6: Upgrade Mode
34
+
35
+ If `secrets-infisical` is found in `.project-context.md` under `## Installed Blueprints`:
36
+
37
+ 1. Read the recorded domain map, environments, and apps wired.
38
+ 2. Re-read the T3 schemas; diff against the recorded shared keys.
39
+ 3. For each NEW var: classify (shared vs app-native), pick its domain, plan a placeholder in `/shared/<domain>` for every env + an env-aware reference in each consuming app folder.
40
+ 4. Present additions; never delete user secrets without confirmation.
41
+ 5. Update `scripts/infisical-map.sh`, re-run the sync script in dry-run, then apply.
42
+ 6. Update `installed_at`; skip to Step 9 (Output Summary).
43
+
44
+ ## Step 0: Preconditions (verify before any changes)
45
+
46
+ - `infisical --version` works (else: `brew install infisical/get-cli/infisical`).
47
+ - `infisical login` done.
48
+ - An Infisical project exists and `infisical init` has produced a committed `.infisical.json` in each app/package dir (workspaceId only — no secrets).
49
+ - The project's environments are known (default `dev`, `staging`, `prod`). Confirm with the user; capture the exact environment **slugs** — these go into the reference prefix and MUST match Infisical's env slugs.
50
+
51
+ ## Step 2: Understand the Target Project
52
+
53
+ - Detect project type (monorepo vs single-repo) — reuse the same detection as scaffold-env (`turbo.json`, `"workspaces"`, `pnpm-workspace.yaml`, etc.).
54
+ - Discover apps/packages that need secrets (`apps/web`, `apps/server`, `packages/db`, …).
55
+ - **Read the T3 env schemas** in each (`env/client.ts`, `env/server.ts`, backend `env.ts`, plus how `packages/db` reads `process.env`) → the full variable list per app.
56
+
57
+ ## Step 3: Derive and Confirm the Plan (do not apply yet)
58
+
59
+ - Classify each var:
60
+ - **app-native** (`NEXT_PUBLIC_*`, `PORT`, single-app config) → real value in the app folder, never referenced.
61
+ - **shared** (DB URLs, API keys, anything >1 app uses, or a domain's canonical key) → goes in `/shared/<domain>` + an env-aware reference in each consuming app.
62
+ - When ambiguous, ask.
63
+ - Group shared vars into domains (derive from installed blueprints + schema groups; see the BLUEPRINT domain table).
64
+ - Build a dry-run plan: folders, shared placeholders, app references, app-native vars left in place.
65
+ - **Present the domain map + plan and ask the user to confirm/edit.** Encourage verbose, self-describing names (`IMAGE_BUCKET_ENDPOINT`, not `STORAGE_ENDPOINT`). Offer an optional rename/cleanup sub-step if the project has vague names or dead vars.
66
+
67
+ ## Step 4: Generate the Sync Script + Data Map
68
+
69
+ - Write `scripts/infisical-sync.sh` (idempotent, dry-run by default, `APPLY=1` to apply, loops the environments list) and `scripts/infisical-map.sh` (the confirmed `ENVIRONMENTS`, `DOMAINS`, `APP_PATHS`, `SHARED_MAP`, `REF_MAP`).
70
+ - The reference helper MUST emit the env-prefixed form, substituting the current loop environment's slug:
71
+
72
+ ```bash
73
+ REF() { printf '${%s.shared.%s.%s}' "$ENV" "$1" "$2"; } # $ENV is the slug
74
+ ```
75
+
76
+ - Shared placeholders use the `REPLACE_ME` sentinel (or sensible non-secret defaults). Never overwrite an existing value.
77
+ - `chmod +x scripts/infisical-sync.sh`.
78
+
79
+ ## Step 5: Apply
80
+
81
+ - Run dry-run (`APPLY=0`), show the plan, get the OK, then run with `APPLY=1`. The script loops every environment.
82
+
83
+ ## Step 6: Wire package.json
84
+
85
+ - Add/adjust `infisical run --path=/apps/<app> --env=<env>` wrappers in dev/run scripts; add `:dev` variants for `packages/db` scripts. Leave production scripts clean (the host injects Infisical).
86
+
87
+ ## Step 7: Verify (do not skip)
88
+
89
+ For at least the dev environment, resolve a reference end to end:
90
+
91
+ ```bash
92
+ infisical run --path=/apps/server --env=dev -- printenv CORE_DATABASE_URL
93
+ ```
94
+
95
+ - Prints a value → good.
96
+ - Prints the literal `${dev.shared...}` → the reference is malformed or the shared key/folder is missing. Fix before continuing. (Most common past failure: the bare `${shared...}` form without the env slug.)
97
+
98
+ ## Step 8: Record + Maintenance Skill
99
+
100
+ Append an entry to `.project-context.md` under `## Installed Blueprints`:
101
+
102
+ ```yaml
103
+ ### secrets-infisical
104
+ blueprint: secrets-infisical
105
+ installed_at: <date>
106
+ choices:
107
+ placeholder_sentinel: REPLACE_ME
108
+ script_language: bash
109
+ environments:
110
+ - <env slugs>
111
+ domain_map:
112
+ <domain>: [<keys>]
113
+ apps_wired:
114
+ - path: <appPath>
115
+ references: [<domains>]
116
+ script_path: scripts/infisical-sync.sh
117
+ ```
118
+
119
+ If `.project-context.md` does not exist, create it with the standard header and the Installed Blueprints section.
120
+
121
+ ### Create Maintenance Skill
122
+
123
+ Append to the shared project-level maintenance skill at `<project>/.claude/skills/project-maintenance/SKILL.md`. If the file does not exist, create it with frontmatter:
124
+
125
+ ```yaml
126
+ ---
127
+ name: project-maintenance
128
+ description: Maintenance rules for all installed blueprints. Invoke when modifying schemas, env vars, auth config, workspace configs, UI components, or Tailwind tokens.
129
+ user-invocable: false
130
+ ---
131
+ ```
132
+
133
+ Then append (skip if `### secrets-infisical maintenance` already exists — do not duplicate):
134
+
135
+ ```markdown
136
+ ### secrets-infisical maintenance
137
+ - Adding a SHARED env var (used by >1 app, or a domain's canonical value):
138
+ 1. Add the key to its `/shared/<domain>` folder in EVERY environment, set to
139
+ `REPLACE_ME` (or a non-secret default).
140
+ 2. Add an environment-aware reference `${<env>.shared.<domain>.<KEY>}` in each
141
+ consuming app/package folder, for every environment.
142
+ 3. Add the key to scripts/infisical-map.sh (SHARED_MAP + REF_MAP) and re-run
143
+ `APPLY=1 ./scripts/infisical-sync.sh`.
144
+ 4. Tell the human which `/shared/<domain>/<KEY>` placeholders to fill.
145
+ - Adding an APP-NATIVE var (PORT, NEXT_PUBLIC_*, single-app config): set it
146
+ directly in that app's Infisical folder as a real value — do NOT create a
147
+ shared key or reference.
148
+ - Reference syntax is ALWAYS `${<env-slug>.shared.<domain>.<KEY>}` where the slug
149
+ matches the environment the reference lives in. The bare `${shared...}` form
150
+ DOES NOT resolve. Verify with:
151
+ `infisical run --path=/apps/<app> --env=dev -- printenv <KEY>`.
152
+ - Rotating a secret: edit the single `/shared/<domain>/<KEY>` value; all
153
+ referencing apps inherit it. Never edit the value in an app folder.
154
+ - Removing a var: delete the shared key AND every app reference to it; remove from
155
+ infisical-map.sh.
156
+ - Never commit real secret values. `.infisical.json` is safe to commit; `.env*`
157
+ stays gitignored.
158
+ ```
159
+
160
+ ## Step 9: Output Summary
161
+
162
+ - List files created/modified, with a one-line description each.
163
+ - Print the full domain map and the per-app reference list.
164
+ - Print the app-native vars as "set directly — not referenced."
165
+ - Print the **`REPLACE_ME` checklist**: every `/shared/<domain>/<KEY>` still set to the placeholder, per environment, so the user knows exactly what to paste into the Infisical dashboard.
166
+ - Remind: real values are pasted in Infisical; the agent never sets them.
167
+
168
+ ## Important Notes
169
+
170
+ - The reference slug MUST match the environment it lives in. The script iterates per env and substitutes `$ENV` into both `--env` and the reference string.
171
+ - Never write real secret values — only placeholders and plumbing.
172
+ - `.infisical.json` is safe to commit; `.env*` stays gitignored.
173
+ - Idempotent: re-running the sync script must be safe — `stub` only sets when empty, `link` just rewrites the same reference.
package/ENGINEERING.md CHANGED
@@ -43,5 +43,5 @@ These preferences apply to ALL blueprints in this repo and to any project scaffo
43
43
  - **Project context:** Every blueprint appends to `.project-context.md` in the target project root — tool-agnostic location readable by any AI coding tool
44
44
  - **Skills live in `.claude/skills/<name>/SKILL.md`** — single source of truth. Installed globally to `~/.claude/skills/`, `~/.config/opencode/skills/`, `~/.cursor/skills/` via the CLI. OpenCode and Cursor also read `~/.claude/skills/` as a cross-tool fallback.
45
45
  - **Rules/preferences are in `ENGINEERING.md`** — tool-specific files (`CLAUDE.md`, `AGENTS.md`, `.cursor/rules/engineering.mdc`) point to it
46
- - **Available skills:** `/new-project`, `/discover`, `/copywrite`, `/copywrite-landing`, `/design-system`, `/scaffold-foundation`, `/scaffold-env`, `/scaffold-tailwind`, `/scaffold-ui`, `/scaffold-auth`, `/scaffold-db`, `/audit`, plus `blueprint-catalog` (background, auto-invoked)
46
+ - **Available skills:** `/new-project`, `/discover`, `/copywrite`, `/copywrite-landing`, `/design-system`, `/scaffold-foundation`, `/scaffold-env`, `/scaffold-tailwind`, `/scaffold-ui`, `/scaffold-auth`, `/scaffold-db`, `/scaffold-infisical`, `/audit`, plus `blueprint-catalog` (background, auto-invoked)
47
47
  - **BLUEPRINT.md required** in every blueprint directory. Placeholder blueprints must still have a BLUEPRINT.md documenting intended scope.
@@ -10,7 +10,9 @@ Complete.
10
10
 
11
11
  ## Problem
12
12
 
13
- Environment variable management is tedious and error-prone across projects. Developers forget to add new variables to `.env.example`, miss the split between client and server vars in Next.js, deploy with missing variables that only crash at runtime, and have no type safety when accessing `process.env`. This blueprint creates a validated, type-safe environment variable system using `@t3-oss/env` + Zod that catches missing or malformed variables at build time.
13
+ Environment variable management is tedious and error-prone across projects. Developers miss the split between client and server vars in Next.js, deploy with missing variables that only crash at runtime, and have no type safety when accessing `process.env`. This blueprint creates a validated, type-safe environment variable system using `@t3-oss/env` + Zod that catches missing or malformed variables at build time.
14
+
15
+ The Zod schema files are the **registry** of what environment variables an app needs — there is no separate `.env.example`. Values are delivered by Infisical (see the `secrets-infisical` blueprint) or the deployment platform; for local work, a developer uses `.env.local` or `infisical run`.
14
16
 
15
17
  This blueprint covers both Next.js apps (client/server split with `@t3-oss/env-nextjs`) and backend apps (server-only with `@t3-oss/env-core`).
16
18
 
@@ -56,9 +58,9 @@ Both files are imported in `next.config.ts` to trigger build-time validation.
56
58
 
57
59
  - **`z.preprocess` for PORT** (recommended) — Environment variables are always strings. PORT needs to be a number. `z.preprocess` handles the string-to-number conversion cleanly.
58
60
 
59
- - **`.env.example` with documentation** (required) — Every app gets a documented `.env.example` with section headers, comments explaining each variable, and local/production value hints. This is the single source of truth for what environment variables an app needs.
61
+ - **Schema files are the registry** (required) — The Zod schema files (`client.ts`, `server.ts`, `env.ts`) are the single source of truth for what environment variables an app needs. Keep them well-commented (purpose, local/production hints) they replace the old `.env.example`. Values come from Infisical or the platform.
60
62
 
61
- - **Empty `.env.local` created** (required) — An empty `.env.local` file is created during scaffolding so developers have the file ready to fill in. This file is gitignored.
63
+ - **Empty `.env.local` created** (required) — An empty `.env.local` file is created during scaffolding so developers have the file ready to fill in for local work. This file is gitignored.
62
64
 
63
65
  ---
64
66
 
@@ -70,7 +72,7 @@ Both files are imported in `next.config.ts` to trigger build-time validation.
70
72
  - Zod for schema validation
71
73
  - Client/server split in Next.js (two files)
72
74
  - Build-time validation via `next.config.ts` import
73
- - `.env.example` for every app
75
+ - Well-commented schema files as the variable registry (no `.env.example`)
74
76
 
75
77
  **Recommended defaults — ask during scaffolding:**
76
78
 
@@ -123,7 +125,6 @@ apps/web/
123
125
  │ ├── client.ts # Client env schema + validation
124
126
  │ └── server.ts # Server env schema + validation
125
127
  ├── next.config.ts # Imports both env files for build-time validation
126
- ├── .env.example # Documented env template
127
128
  └── .env.local # Local values (gitignored)
128
129
  ```
129
130
 
@@ -152,7 +153,6 @@ apis/server/
152
153
  ├── src/
153
154
  │ ├── env.ts # Server env schema + validation
154
155
  │ └── index.ts # Imports env.ts at top
155
- ├── .env.example # Documented env template
156
156
  └── .env.local # Local values (gitignored)
157
157
  ```
158
158
 
@@ -175,7 +175,6 @@ For standalone applications (Next.js or backend) without a monorepo, the pattern
175
175
  |--------|----------|-------------|
176
176
  | Next.js env location | `apps/web/src/env/` | `src/env/` |
177
177
  | Backend env location | `apis/server/src/env.ts` | `src/env.ts` |
178
- | `.env.example` location | Per-app (e.g., `apps/web/.env.example`) | Project root `.env.example` |
179
178
  | `.env.local` location | Per-app (e.g., `apps/web/.env.local`) | Project root `.env.local` |
180
179
  | Import path | `~/env/server` or `~/env/client` | `~/env/server` or `@/env/server` |
181
180
 
@@ -186,7 +185,7 @@ All core patterns are project-type-agnostic:
186
185
  - Zod schemas for validation
187
186
  - Client/server split (two files) for Next.js
188
187
  - Build-time validation via `next.config.ts` import
189
- - `.env.example` documentation pattern
188
+ - Well-commented schema files as the variable registry
190
189
  - `emptyStringAsUndefined: true`
191
190
 
192
191
  ---
@@ -197,10 +196,8 @@ All core patterns are project-type-agnostic:
197
196
  |---|---|---|---|
198
197
  | `files/nextjs/env/client.ts` | Next.js client env schema | `apps/web/src/env/client.ts` | `src/env/client.ts` |
199
198
  | `files/nextjs/env/server.ts` | Next.js server env schema | `apps/web/src/env/server.ts` | `src/env/server.ts` |
200
- | `files/nextjs/.env.example` | Documented env template (Next.js) | `apps/web/.env.example` | `.env.example` |
201
199
  | `files/nextjs/.env.local` | Empty local env file (Next.js) | `apps/web/.env.local` | `.env.local` |
202
200
  | `files/server/env.ts` | Backend env schema (`@t3-oss/env-core`) | `apis/server/src/env.ts` | `src/env.ts` |
203
- | `files/server/.env.example` | Documented env template (backend) | `apis/server/.env.example` | `.env.example` |
204
201
  | `files/server/.env.local` | Empty local env file (backend) | `apis/server/.env.local` | `.env.local` |
205
202
 
206
203
  ---
@@ -222,29 +219,27 @@ All core patterns are project-type-agnostic:
222
219
  import "./src/env/client";
223
220
  ```
224
221
  If existing `import "./env"` is present, replace it with the two new imports.
225
- 6. Copy `.env.example` — replace `{{APP_NAME}}`
226
- 7. Create `.env.local` (empty)
227
- 8. If other blueprints are already installed, pre-populate their env vars into the appropriate schema files
222
+ 6. Create `.env.local` (empty)
223
+ 7. If other blueprints are already installed, pre-populate their env vars into the appropriate schema files
228
224
 
229
225
  ### Phase 2: Backend App Setup (if present)
230
226
 
231
- 9. Copy `env.ts` to `src/env.ts` in the backend app — replace `{{APP_NAME}}`
232
- 10. Update the app's entry point (e.g., `src/index.ts`) to import `env.ts`:
233
- ```typescript
234
- import { env } from "./env.js";
235
- ```
236
- 11. Copy `.env.example` — replace `{{APP_NAME}}`
237
- 12. Create `.env.local` (empty)
227
+ 8. Copy `env.ts` to `src/env.ts` in the backend app — replace `{{APP_NAME}}`
228
+ 9. Update the app's entry point (e.g., `src/index.ts`) to import `env.ts`:
229
+ ```typescript
230
+ import { env } from "./env.js";
231
+ ```
232
+ 10. Create `.env.local` (empty)
238
233
 
239
234
  ### Phase 3: Dependency Installation
240
235
 
241
- 13. For each Next.js app: ensure `@t3-oss/env-nextjs` and `zod` are in `dependencies`
242
- 14. For each backend app: ensure `@t3-oss/env-core`, `zod`, and `dotenv` are in `dependencies`
236
+ 11. For each Next.js app: ensure `@t3-oss/env-nextjs` and `zod` are in `dependencies`
237
+ 12. For each backend app: ensure `@t3-oss/env-core`, `zod`, and `dotenv` are in `dependencies`
243
238
 
244
239
  ### Phase 4: Verification
245
240
 
246
- 15. Run `bun dev` (or equivalent) — env validation should run without errors
247
- 16. Try removing a required variable from `.env.local` — build should fail with a clear error message
241
+ 13. Run `bun dev` (or equivalent) — env validation should run without errors
242
+ 14. Try removing a required variable from `.env.local` — build should fail with a clear error message
248
243
 
249
244
  ---
250
245
 
@@ -254,22 +249,22 @@ All core patterns are project-type-agnostic:
254
249
 
255
250
  | When you... | Then do... | Why |
256
251
  |---|---|---|
257
- | Add a new env var in code | Add to `env/client.ts` or `env/server.ts` schema AND `.env.example` AND `.env.local` | Missing from schema = build-time crash; missing from example = teammates can't set up |
252
+ | Add a new env var in code | Add to the `env/client.ts` or `env/server.ts` schema (the registry) and, if used locally, `.env.local` | Missing from schema = build-time crash; the schema documents what's needed |
258
253
  | Add a `NEXT_PUBLIC_*` var | Put it in `env/client.ts` with both `client` schema and `runtimeEnv` entries | Client vars must be explicitly mapped in Next.js |
259
254
  | Add a server-only var to Next.js | Put it in `env/server.ts` (uses `experimental__runtimeEnv: process.env`) | Avoids listing every server var twice |
260
- | Remove an env var | Remove from schema file, `.env.example`, and `.env.local` | Stale vars cause confusion |
255
+ | Remove an env var | Remove from the schema file and `.env.local` | Stale vars cause confusion |
261
256
  | Add a new app to the monorepo | Create its own `env/` files — don't share env schemas between apps | Each app has different variable requirements |
262
- | Change a var from optional to required | Update the Zod schema (remove `.optional()`) and ensure `.env.example` has a value | Will break builds for teammates with empty values |
257
+ | Change a var from optional to required | Update the Zod schema (remove `.optional()`) and ensure the value is provided (Infisical/platform/`.env.local`) | Will break builds where the value is empty |
263
258
 
264
259
  ### Condensed Rules for project rules file
265
260
 
266
261
  ```markdown
267
262
  ### env-t3 maintenance
268
- - After adding a new env var: add it to the env schema file (client.ts or server.ts), `.env.example`, and `.env.local`
263
+ - The Zod schema files (client.ts/server.ts/env.ts) are the registry of env vars keep them well-commented. There is no .env.example; values come from Infisical or the platform.
264
+ - After adding a new env var: add it to the env schema file (client.ts or server.ts) and, if used locally, .env.local
269
265
  - NEXT_PUBLIC_* vars go in env/client.ts with explicit runtimeEnv mapping; server vars go in env/server.ts
270
- - After removing an env var: remove from schema, .env.example, and .env.local
266
+ - After removing an env var: remove from the schema and .env.local
271
267
  - Never access process.env directly — always use the typed env objects (clientEnv, serverEnv, or env)
272
- - Keep .env.example documented with section headers and local/production hints
273
268
  ```
274
269
 
275
270
  ---
@@ -288,7 +283,7 @@ All core patterns are project-type-agnostic:
288
283
  - **Client/server split** — Next.js apps always get two files, not one combined file
289
284
  - **Build-time validation** — env files are always imported in `next.config.ts`
290
285
  - **`emptyStringAsUndefined`** — always enabled (prevents empty string foot-guns)
291
- - **`.env.example` documentation** always includes section headers and comments
286
+ - **Schema as registry** — the Zod schema files are the documented source of truth for env vars; no `.env.example` (values come from Infisical or the platform)
292
287
 
293
288
  ## Project Context Output
294
289
 
@@ -307,13 +302,11 @@ apps_configured:
307
302
  files:
308
303
  - src/env/client.ts
309
304
  - src/env/server.ts
310
- - .env.example
311
305
  - .env.local
312
306
  - name: server
313
307
  type: backend
314
308
  files:
315
309
  - src/env.ts
316
- - .env.example
317
310
  - .env.local
318
311
  packages_added:
319
312
  - "@t3-oss/env-nextjs"
@@ -0,0 +1,295 @@
1
+ # Secrets Management Blueprint — Infisical /shared + Env-Aware References
2
+
3
+ ## Tier
4
+
5
+ Feature
6
+
7
+ ## Status
8
+
9
+ Complete.
10
+
11
+ ## Problem
12
+
13
+ Multi-app monorepos duplicate the same secret (DB URLs, API keys) across every app's env, so a rotation means editing N places and inevitably drifting. Local `.env` files also have to be copied into every fresh clone / workspace, and there's no single place that says "this is the canonical value." This blueprint centralizes every shared secret **once** in Infisical under a domain-grouped `/shared` folder, and has each app reference those values, so a single change propagates everywhere and a fresh checkout needs only `infisical login` + the committed `.infisical.json`.
14
+
15
+ The agent does all the plumbing (folders, placeholders, references, `infisical run` wiring); the human's only remaining job is to paste real secret values over the placeholders in the Infisical dashboard. The agent never invents or guesses secret values.
16
+
17
+ ## Blueprint Dependencies
18
+
19
+ - **Required:** `env-t3` — the T3 Zod schemas (`env/client.ts`, `env/server.ts`, backend `env.ts`) are the source of truth for which variables exist; this blueprint reads them to build the Infisical structure. If `env-t3` is not installed, run `/scaffold-env` first.
20
+ - **Recommended:** `db-drizzle-postgres`, `auth-better-auth` — they define the natural domains (databases, auth) and their variables.
21
+
22
+ ### Interaction with Other Blueprints
23
+
24
+ - `env-t3` defines *what* vars exist + validation. This blueprint defines *where the values live and how they're delivered*. They are complementary layers — `env-t3` stays usable without Infisical (some projects deliver secrets via Vercel/Railway directly), and Infisical cleanly **consumes** the T3 schemas instead of bloating them.
25
+ - When a feature blueprint adds an env var to a T3 schema, the maintenance rules here sync it into Infisical as a placeholder. The T3 schema change and the Infisical change are two halves of the same edit.
26
+
27
+ ---
28
+
29
+ ## Architecture
30
+
31
+ ### Core Pattern
32
+
33
+ - Canonical **values** live exactly once in `/shared/<domain>/<KEY>` (placeholders until a human fills them).
34
+ - Apps/packages hold **environment-aware references**: `${<ENV_SLUG>.shared.<domain>.<KEY>}` — never raw values for shared vars.
35
+ - **App-native** vars (`PORT`, `NEXT_PUBLIC_*`, langfuse keys, etc.) stay as real values in the app folder, never referenced.
36
+ - The CLI delivers secrets at launch via `infisical run --path=<app> --env=<env>`.
37
+
38
+ Change a shared value once → every app that references it sees the new value. No copy-paste drift across `apps/server`, `apps/web`, `packages/db`.
39
+
40
+ ### Reference Syntax (CRITICAL)
41
+
42
+ References MUST be environment-prefixed, and the slug MUST match the environment the reference lives in:
43
+
44
+ ```
45
+ ${dev.shared.pipeline.PIPELINE_VERSION} # in the dev environment
46
+ ${staging.shared.pipeline.PIPELINE_VERSION} # in the staging environment
47
+ ${prod.shared.pipeline.PIPELINE_VERSION} # in the prod environment
48
+ ```
49
+
50
+ The **same key** therefore has a **different literal value in each environment** — each points at its own environment's shared subtree.
51
+
52
+ The bare form `${shared.<domain>.<KEY>}` **DOES NOT RESOLVE** (this is the #1 past failure). Other wrong forms:
53
+
54
+ ```
55
+ ${shared.pipeline.PIPELINE_VERSION} ← MISSING env slug — does not resolve
56
+ ${env.shared.pipeline.PIPELINE_VERSION} ← literal "env" — wrong
57
+ ${/shared/pipeline/PIPELINE_VERSION} ← slash path form — wrong for this case
58
+ ```
59
+
60
+ Verify with the §Verification check below — the reference must print its value, not the literal `${...}`.
61
+
62
+ ### Domains
63
+
64
+ `/shared` is subdivided by **concern** (not by app), derived from installed blueprints + the T3 schemas: e.g. `core`, `auth`, `pipeline`, `knowledge-base`, `image-storage`, `ai`, `search`, `infra`. Domains are a soft taxonomy; the user confirms the map before anything is applied.
65
+
66
+ | Source signal | Domain(s) created |
67
+ |---|---|
68
+ | `db-drizzle-postgres` installed, schema groups | one domain per DB group (`core`, `auth`, `pipeline`, …), each with a `*_DATABASE_URL` |
69
+ | `auth-better-auth` installed | `auth` (`BETTER_AUTH_*`, `GOOGLE_*`, `RESEND_EMAIL_API_KEY`) |
70
+ | Object storage / S3-style vars present | `image-storage`, `knowledge-base` (bucket keys) |
71
+ | LLM / model API keys present | `ai` |
72
+ | Web search / scraping / media API keys present | `search` |
73
+ | Redis / queue / cache | `infra` |
74
+ | Anything not classifiable | `core` (catch-all) or ask |
75
+
76
+ ### Placeholders
77
+
78
+ Every shared secret is created with the `REPLACE_ME` sentinel (caps-with-underscore, trivially greppable in the UI). Sensible **non-secret** defaults are allowed for clearly non-sensitive config (`KB_QUEUE_NAMESPACE=pgboss_<env>`, `KB_ENABLE_RERANK=false`, `*_PUBLIC_BASE_URL=https://replace-me.example.com`). Anything that is an actual secret (keys, passwords, connection strings) → `REPLACE_ME`. The sync script only stubs a value if it is currently empty — it never overwrites a filled value.
79
+
80
+ ### Verbose, Meaningful Names
81
+
82
+ The blueprint encourages self-describing variable names with the domain/purpose embedded: `STORAGE_ENDPOINT` → `IMAGE_BUCKET_ENDPOINT`, `RESEND_API_KEY` → `RESEND_EMAIL_API_KEY`, `SYNTHETIC_API_KEY` → `SYNTHETIC_AI_API_KEY`, `PARALLEL_API_KEY` → `PARALLEL_RESEARCH_API_KEY`. The skill offers an optional rename/cleanup step.
83
+
84
+ ---
85
+
86
+ ## Hard Requirements vs Recommended Defaults
87
+
88
+ **Hard requirements:**
89
+ - Infisical CLI + a linked project (`.infisical.json` committed per app/package — holds no secrets).
90
+ - `/shared/<domain>` canonical values; app folders hold env-aware references.
91
+ - Environment-prefixed reference syntax (the slug matches the environment the reference lives in).
92
+ - An idempotent, dry-run-by-default sync script committed to the repo.
93
+ - `infisical run --path --env` wrappers in dev/run scripts.
94
+
95
+ **Recommended defaults — ask during scaffolding:**
96
+
97
+ | Choice | Recommended | Alternatives |
98
+ |---|---|---|
99
+ | Environments | `dev`, `staging`, `prod` | project-specific set (use the real Infisical slugs) |
100
+ | Placeholder sentinel | `REPLACE_ME` | per-key sentinel `REPLACE_ME_<KEY>`; key-name-as-value |
101
+ | Sync script language | bash (uses Infisical CLI, zero deps) | TypeScript |
102
+ | Domain map | derived from blueprints/schemas, user confirms | fully manual |
103
+
104
+ ---
105
+
106
+ ## Dependencies
107
+
108
+ ### Tooling
109
+
110
+ | Tool | Install | Purpose |
111
+ |---|---|---|
112
+ | Infisical CLI | `brew install infisical/get-cli/infisical` | Create folders/secrets, deliver them via `infisical run` |
113
+
114
+ ### npm packages
115
+
116
+ None required at runtime — delivery is via the CLI wrapper. `env-t3` already provides the validation layer.
117
+
118
+ ### Environment Variables
119
+
120
+ This blueprint creates no new variables of its own. It mirrors the variables already declared in the project's T3 env schemas into Infisical.
121
+
122
+ ---
123
+
124
+ ## Monorepo Wiring
125
+
126
+ - `.infisical.json` committed in each of `apps/*`, `packages/*` that need secrets (workspaceId only — no secrets).
127
+ - `package.json` dev scripts wrapped in `infisical run --path=/apps/<app> --env=dev`:
128
+ ```jsonc
129
+ // apps/server
130
+ "dev": "infisical run --path=/apps/server --env=dev -- bun --watch src/index.ts"
131
+ // apps/web
132
+ "dev": "infisical run --path=/apps/web --env=dev -- bun run --bun next dev"
133
+ // packages/db — a :dev variant per script that needs DB envs
134
+ "core:migrate:dev": "infisical run --env=dev --path=/packages/db -- bun run core:migrate"
135
+ ```
136
+ - Production scripts stay clean — Railway/the host injects Infisical at the platform level.
137
+
138
+ ## Single-Repo Adaptation
139
+
140
+ One app folder (e.g. `/app`) plus `/shared`. Same env-aware references; same placeholder + sync-script approach. `infisical run --path=/app --env=dev`. The `APP_PATHS` and `REF_MAP` in the data map collapse to the single app.
141
+
142
+ ---
143
+
144
+ ## File Manifest
145
+
146
+ | File | Purpose | Target |
147
+ |---|---|---|
148
+ | `files/scripts/infisical-sync.sh` | Idempotent, dry-run-by-default structure sync; loops all environments | `scripts/infisical-sync.sh` |
149
+ | `files/scripts/infisical-map.sh` | Data map: `ENVIRONMENTS`, `DOMAINS`, `APP_PATHS`, `SHARED_MAP`, `REF_MAP` (generated per project) | `scripts/infisical-map.sh` |
150
+ | `files/docs/INFISICAL.md` | Runbook: setup, reference syntax, verification, maintenance | `docs/INFISICAL.md` |
151
+
152
+ ---
153
+
154
+ ## Integration Steps
155
+
156
+ ### Phase 0: Preconditions
157
+
158
+ 1. Confirm Infisical CLI installed (`infisical --version`); else `brew install infisical/get-cli/infisical`.
159
+ 2. Confirm `infisical login` done.
160
+ 3. Confirm an Infisical project exists and `infisical init` produced a committed `.infisical.json` in each app/package dir.
161
+ 4. Confirm `env-t3` is installed (read `.project-context.md`). If not, run `/scaffold-env` first.
162
+ 5. Capture the real environment **slugs** (default `dev`, `staging`, `prod`) — these go into the reference prefix and MUST match Infisical exactly.
163
+
164
+ ### Phase 1: Read Sources of Truth
165
+
166
+ 6. Read the T3 env schemas in every app/package → full variable list per app.
167
+ 7. Read `.project-context.md` → installed blueprints, app layout, npm scope.
168
+
169
+ ### Phase 2: Derive and Confirm the Plan (do not apply yet)
170
+
171
+ 8. Classify each var: shared-reference vs app-native.
172
+ 9. Group shared vars into domains (table above).
173
+ 10. Produce a dry-run plan: folders, shared placeholders, app references, app-native vars left in place.
174
+ 11. **Present the domain map + plan and ask the user to confirm/edit.**
175
+
176
+ ### Phase 3: Generate the Sync Script + Data Map
177
+
178
+ 12. Write `scripts/infisical-sync.sh` and `scripts/infisical-map.sh` from the templates, filling the data map from the confirmed plan.
179
+
180
+ ### Phase 4: Apply
181
+
182
+ 13. Run dry-run, show output, then (on user OK) re-run with `APPLY=1`. The script loops every environment.
183
+
184
+ ### Phase 5: Wire package.json
185
+
186
+ 14. Add/adjust `infisical run --path=... --env=...` wrappers in dev/run scripts; add `:dev` variants for `packages/db` scripts. Leave production scripts clean.
187
+
188
+ ### Phase 6: Verify (do not skip)
189
+
190
+ 15. Resolve a reference end to end for at least `dev`:
191
+ ```bash
192
+ infisical run --path=/apps/server --env=dev -- printenv CORE_DATABASE_URL
193
+ ```
194
+ Prints a value → good. Prints the literal `${dev.shared...}` → the reference is malformed or the shared key/folder is missing. Fix before continuing.
195
+
196
+ ### Phase 7: Record + Summarize
197
+
198
+ 16. Update `.project-context.md` and the project-maintenance skill.
199
+ 17. Print files created, the full domain map, and the **`REPLACE_ME` checklist** the user must fill in Infisical.
200
+
201
+ ---
202
+
203
+ ## Maintenance Hooks
204
+
205
+ ### Secrets Hooks
206
+
207
+ | When you... | Then do... | Why |
208
+ |---|---|---|
209
+ | Add a **shared** env var to a T3 schema | Add the key to `/shared/<domain>` (placeholder) in every env, add an env-aware reference in each consuming app folder, update `infisical-map.sh`, re-run the sync script | Keeps the single source of truth and every consumer in sync |
210
+ | Add an **app-native** var | Set it directly in that app's Infisical folder as a real value — no shared key, no reference | App-native vars don't belong in `/shared` |
211
+ | Add a new environment | Add its slug to `ENVIRONMENTS` and re-run the sync script | Folders + references are per-environment |
212
+ | Rotate a secret | Edit the single `/shared/<domain>/<KEY>` value; all apps inherit it | Never edit a value in an app folder — it should be a reference |
213
+ | Remove a var | Delete the shared key AND every app reference; remove from `infisical-map.sh` | Stale references print the literal `${...}` |
214
+
215
+ ### Condensed Rules for project rules file
216
+
217
+ ```markdown
218
+ ### secrets-infisical maintenance
219
+ - Adding a SHARED env var (used by >1 app, or a domain's canonical value):
220
+ 1. Add the key to its `/shared/<domain>` folder in EVERY environment, set to
221
+ `REPLACE_ME` (or a non-secret default).
222
+ 2. Add an environment-aware reference `${<env>.shared.<domain>.<KEY>}` in each
223
+ consuming app/package folder, for every environment.
224
+ 3. Add the key to scripts/infisical-map.sh (SHARED_MAP + REF_MAP) and re-run
225
+ `APPLY=1 ./scripts/infisical-sync.sh`.
226
+ 4. Tell the human which `/shared/<domain>/<KEY>` placeholders to fill.
227
+ - Adding an APP-NATIVE var (PORT, NEXT_PUBLIC_*, single-app config): set it
228
+ directly in that app's Infisical folder as a real value — do NOT create a
229
+ shared key or reference.
230
+ - Reference syntax is ALWAYS `${<env-slug>.shared.<domain>.<KEY>}` where the slug
231
+ matches the environment the reference lives in. The bare `${shared...}` form
232
+ DOES NOT resolve. Verify with:
233
+ `infisical run --path=/apps/<app> --env=dev -- printenv <KEY>`.
234
+ - Rotating a secret: edit the single `/shared/<domain>/<KEY>` value; all
235
+ referencing apps inherit it. Never edit the value in an app folder.
236
+ - Removing a var: delete the shared key AND every app reference to it; remove from
237
+ infisical-map.sh.
238
+ - Never commit real secret values. `.infisical.json` is safe to commit; `.env*`
239
+ stays gitignored.
240
+ ```
241
+
242
+ ---
243
+
244
+ ## What's Configurable
245
+
246
+ - Environments list (slugs)
247
+ - Domain map (the soft taxonomy)
248
+ - Placeholder sentinel
249
+ - Which apps/packages are wired
250
+ - Sync script language (bash vs TS)
251
+
252
+ ## What's Opinionated
253
+
254
+ - **Infisical** as the secret store.
255
+ - **`/shared` + env-aware references** as the single-source-of-truth mechanism.
256
+ - **Environment-prefixed reference syntax** — non-negotiable; it's the only form that resolves.
257
+ - **Placeholder-first, human-fills-values** workflow — the agent never writes real secret values.
258
+
259
+ ## Project Context Output
260
+
261
+ Appends to `.project-context.md` under `## Installed Blueprints`:
262
+
263
+ ```yaml
264
+ ### secrets-infisical
265
+ blueprint: secrets-infisical
266
+ installed_at: <date>
267
+ choices:
268
+ placeholder_sentinel: REPLACE_ME
269
+ script_language: bash
270
+ environments:
271
+ - dev
272
+ - staging
273
+ - prod
274
+ domain_map:
275
+ core: [CORE_DATABASE_URL]
276
+ auth: [AUTH_DATABASE_URL, BETTER_AUTH_URL, BETTER_AUTH_SECRET, GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET, RESEND_EMAIL_API_KEY]
277
+ # ... one entry per domain
278
+ apps_wired:
279
+ - path: /apps/server
280
+ references: [core, auth, ...]
281
+ - path: /apps/web
282
+ references: [ai]
283
+ - path: /packages/db
284
+ references: [core, auth, pipeline, knowledge-base]
285
+ script_path: scripts/infisical-sync.sh
286
+ ```
287
+
288
+ ---
289
+
290
+ ## References
291
+
292
+ - **Infisical secret references:** https://infisical.com/docs/documentation/platform/secret-reference
293
+ - **Infisical CLI:** https://infisical.com/docs/cli/overview
294
+ - **`infisical run`:** https://infisical.com/docs/cli/commands/run
295
+ - **`infisical secrets`:** https://infisical.com/docs/cli/commands/secrets
@@ -0,0 +1,119 @@
1
+ # Infisical Secrets Runbook
2
+
3
+ How secrets work in this project, and how to keep them in sync.
4
+
5
+ ## The model
6
+
7
+ - **Canonical values live once** in Infisical under `/shared/<domain>/<KEY>`.
8
+ - **Apps and packages hold references**, never raw values, for anything shared.
9
+ - **App-native values** (`PORT`, `NEXT_PUBLIC_*`, etc.) live directly in the app's
10
+ Infisical folder.
11
+ - Secrets are delivered at launch by the Infisical CLI:
12
+ `infisical run --path=<app> --env=<env> -- <command>`.
13
+
14
+ Change a shared value once and every referencing app inherits it — no drift.
15
+
16
+ ## Reference syntax (read this before editing anything)
17
+
18
+ References are **environment-prefixed**. The slug MUST match the environment the
19
+ reference lives in:
20
+
21
+ ```
22
+ ${dev.shared.pipeline.PIPELINE_VERSION} # stored in the dev environment
23
+ ${staging.shared.pipeline.PIPELINE_VERSION} # stored in the staging environment
24
+ ${prod.shared.pipeline.PIPELINE_VERSION} # stored in the prod environment
25
+ ```
26
+
27
+ The same key has a different literal value per environment — each points at its
28
+ own environment's `/shared` subtree.
29
+
30
+ **Wrong forms (do not use — they silently fail to resolve):**
31
+
32
+ ```
33
+ ${shared.pipeline.PIPELINE_VERSION} ← missing env slug (the classic bug)
34
+ ${env.shared.pipeline.PIPELINE_VERSION} ← literal "env"
35
+ ${/shared/pipeline/PIPELINE_VERSION} ← slash path form
36
+ ```
37
+
38
+ ## First-time setup
39
+
40
+ 1. Install the CLI: `brew install infisical/get-cli/infisical`
41
+ 2. `infisical login`
42
+ 3. Ensure each app/package has a committed `.infisical.json` (from `infisical init`).
43
+ It holds the workspace id only — no secrets — and is safe to commit.
44
+ 4. Run the sync script to create the structure (see below).
45
+ 5. Open the Infisical dashboard and paste real values over every `REPLACE_ME`
46
+ placeholder in `/shared/<domain>`.
47
+
48
+ ## The sync script
49
+
50
+ `scripts/infisical-sync.sh` creates and maintains the `/shared` structure and all
51
+ app references. It is **dry-run by default** and **idempotent** (never overwrites a
52
+ filled value):
53
+
54
+ ```bash
55
+ # from a dir with .infisical.json (e.g. apps/server)
56
+ APPLY=0 ./scripts/infisical-sync.sh # dry run — prints the plan, changes nothing
57
+ APPLY=1 ./scripts/infisical-sync.sh # apply to every environment in ENVIRONMENTS
58
+ ```
59
+
60
+ The plan lives in `scripts/infisical-map.sh` (`ENVIRONMENTS`, `DOMAINS`,
61
+ `APP_PATHS`, `SHARED_MAP`, `REF_MAP`). Edit that file to add/remove vars.
62
+
63
+ ## Verification
64
+
65
+ After applying, resolve a reference end to end:
66
+
67
+ ```bash
68
+ infisical run --path=/apps/server --env=dev -- printenv CORE_DATABASE_URL
69
+ ```
70
+
71
+ - ✅ prints the value (or `REPLACE_ME`) → the reference resolved correctly.
72
+ - ❌ prints the literal `${dev.shared.core.CORE_DATABASE_URL}` → the reference is
73
+ malformed or the shared key/folder is missing. Fix before proceeding (almost
74
+ always a missing shared key or a bare `${shared...}` reference).
75
+
76
+ ## Running apps with secrets
77
+
78
+ ```jsonc
79
+ // apps/server
80
+ "dev": "infisical run --path=/apps/server --env=dev -- bun --watch src/index.ts"
81
+ // apps/web
82
+ "dev": "infisical run --path=/apps/web --env=dev -- bun run --bun next dev"
83
+ // packages/db — a :dev variant per script that needs DB envs
84
+ "core:migrate:dev": "infisical run --env=dev --path=/packages/db -- bun run core:migrate"
85
+ ```
86
+
87
+ Production scripts stay clean — the host (e.g. Railway) injects Infisical at the
88
+ platform level.
89
+
90
+ ## Maintenance
91
+
92
+ **Add a shared var** (used by >1 app, or a domain's canonical value):
93
+ 1. Add `"domain KEY"` to `SHARED_MAP` and `"appPath domain KEY"` to `REF_MAP` in
94
+ `scripts/infisical-map.sh`.
95
+ 2. Run `APPLY=1 ./scripts/infisical-sync.sh`.
96
+ 3. Paste the real value over the new `/shared/<domain>/<KEY>` placeholder.
97
+
98
+ **Add an app-native var:** set it directly in that app's Infisical folder as a
99
+ real value. Do not add it to the map.
100
+
101
+ **Add an environment:** add its slug to `ENVIRONMENTS` and re-run the sync.
102
+
103
+ **Rotate a secret:** edit the single `/shared/<domain>/<KEY>` value. Every
104
+ referencing app inherits it. Never edit the value in an app folder.
105
+
106
+ **Remove a var:** delete the shared key and every app reference, and remove it
107
+ from `infisical-map.sh`.
108
+
109
+ ## Gotchas
110
+
111
+ - **Env slugs must match Infisical exactly.** If your envs are
112
+ Development/Staging/Production with slugs `dev`/`staging`/`prod`, the references
113
+ use `dev`/`staging`/`prod`.
114
+ - **Folders are per-environment.** The tree exists in every environment; the sync
115
+ loops them all.
116
+ - **Local override foot-gun:** a local `.env.local` applied with `override: true`
117
+ beats Infisical. Watch for stale local values masking the real ones.
118
+ - **Never commit real secret values.** `.infisical.json` is safe to commit;
119
+ `.env*` stays gitignored.
@@ -0,0 +1,115 @@
1
+ #!/usr/bin/env bash
2
+ # Data map for infisical-sync.sh — generated by /scaffold-infisical, edit freely.
3
+ #
4
+ # This file is the per-project plan. The example below is the worked reference
5
+ # shape (a multi-app monorepo); /scaffold-infisical regenerates it from YOUR
6
+ # project's T3 env schemas + installed blueprints. Treat it as a template, not a
7
+ # hardcode.
8
+ #
9
+ # Four arrays drive the sync:
10
+ # ENVIRONMENTS — the env slugs (MUST match Infisical exactly)
11
+ # DOMAINS — the /shared subfolders
12
+ # APP_PATHS — app/package folders that hold references
13
+ # SHARED_MAP — "domain KEY [non-secret default]" (omit default -> REPLACE_ME)
14
+ # REF_MAP — "appPath domain KEY" (the env-aware reference is generated)
15
+
16
+ ENVIRONMENTS=(dev staging prod)
17
+
18
+ DOMAINS=(core auth pipeline knowledge-base image-storage ai search infra)
19
+
20
+ APP_PATHS=(/apps/server /apps/web /packages/db)
21
+
22
+ # "domain KEY [non-secret default]" — omit default for real secrets (-> REPLACE_ME)
23
+ SHARED_MAP=(
24
+ "core CORE_DATABASE_URL"
25
+ "auth AUTH_DATABASE_URL"
26
+ "auth BETTER_AUTH_URL"
27
+ "auth BETTER_AUTH_SECRET"
28
+ "auth GOOGLE_CLIENT_ID"
29
+ "auth GOOGLE_CLIENT_SECRET"
30
+ "auth RESEND_EMAIL_API_KEY"
31
+ "pipeline PIPELINE_DATABASE_URL"
32
+ "pipeline PIPELINE_VERSION"
33
+ "knowledge-base KB_DATABASE_URL"
34
+ "knowledge-base KB_BUCKET_ENDPOINT"
35
+ "knowledge-base KB_BUCKET_NAME"
36
+ "knowledge-base KB_BUCKET_ACCESS_KEY_ID"
37
+ "knowledge-base KB_BUCKET_SECRET_ACCESS_KEY"
38
+ "knowledge-base KB_BUCKET_REGION"
39
+ "knowledge-base KB_QUEUE_NAMESPACE pgboss_dev"
40
+ "knowledge-base KB_ENABLE_RERANK false"
41
+ "image-storage IMAGE_BUCKET_ENDPOINT"
42
+ "image-storage IMAGE_BUCKET_NAME"
43
+ "image-storage IMAGE_BUCKET_ACCESS_KEY_ID"
44
+ "image-storage IMAGE_BUCKET_SECRET_ACCESS_KEY"
45
+ "image-storage IMAGE_BUCKET_PUBLIC_BASE_URL https://replace-me.example.com"
46
+ "ai OPENROUTER_API_KEY"
47
+ "ai OPENROUTER_DEFAULT_MODEL"
48
+ "ai SYNTHETIC_AI_API_KEY"
49
+ "ai MISTRAL_OCR_API_KEY"
50
+ "ai FIREWORKS_AI_API_KEY"
51
+ "ai JINA_PAGE_EXTRACTOR_API_KEY"
52
+ "search PARALLEL_RESEARCH_API_KEY"
53
+ "search TAVILY_API_KEY"
54
+ "search EXA_API_KEY"
55
+ "search PEXELS_API_KEY"
56
+ "search UNSPLASH_API_KEY"
57
+ "search BRAVE_SEARCH_API_KEY"
58
+ "search SERPER_API_KEY"
59
+ "infra REDIS_URL"
60
+ )
61
+
62
+ # "appPath domain KEY" — the env-aware reference is generated automatically
63
+ REF_MAP=(
64
+ "/apps/server core CORE_DATABASE_URL"
65
+ "/apps/server auth AUTH_DATABASE_URL"
66
+ "/apps/server auth BETTER_AUTH_URL"
67
+ "/apps/server auth BETTER_AUTH_SECRET"
68
+ "/apps/server auth GOOGLE_CLIENT_ID"
69
+ "/apps/server auth GOOGLE_CLIENT_SECRET"
70
+ "/apps/server auth RESEND_EMAIL_API_KEY"
71
+ "/apps/server pipeline PIPELINE_DATABASE_URL"
72
+ "/apps/server pipeline PIPELINE_VERSION"
73
+ "/apps/server knowledge-base KB_DATABASE_URL"
74
+ "/apps/server knowledge-base KB_BUCKET_ENDPOINT"
75
+ "/apps/server knowledge-base KB_BUCKET_NAME"
76
+ "/apps/server knowledge-base KB_BUCKET_ACCESS_KEY_ID"
77
+ "/apps/server knowledge-base KB_BUCKET_SECRET_ACCESS_KEY"
78
+ "/apps/server knowledge-base KB_BUCKET_REGION"
79
+ "/apps/server knowledge-base KB_QUEUE_NAMESPACE"
80
+ "/apps/server knowledge-base KB_ENABLE_RERANK"
81
+ "/apps/server image-storage IMAGE_BUCKET_ENDPOINT"
82
+ "/apps/server image-storage IMAGE_BUCKET_NAME"
83
+ "/apps/server image-storage IMAGE_BUCKET_ACCESS_KEY_ID"
84
+ "/apps/server image-storage IMAGE_BUCKET_SECRET_ACCESS_KEY"
85
+ "/apps/server image-storage IMAGE_BUCKET_PUBLIC_BASE_URL"
86
+ "/apps/server ai OPENROUTER_API_KEY"
87
+ "/apps/server ai OPENROUTER_DEFAULT_MODEL"
88
+ "/apps/server ai SYNTHETIC_AI_API_KEY"
89
+ "/apps/server ai MISTRAL_OCR_API_KEY"
90
+ "/apps/server ai FIREWORKS_AI_API_KEY"
91
+ "/apps/server ai JINA_PAGE_EXTRACTOR_API_KEY"
92
+ "/apps/server search PARALLEL_RESEARCH_API_KEY"
93
+ "/apps/server search TAVILY_API_KEY"
94
+ "/apps/server search EXA_API_KEY"
95
+ "/apps/server search PEXELS_API_KEY"
96
+ "/apps/server search UNSPLASH_API_KEY"
97
+ "/apps/server search BRAVE_SEARCH_API_KEY"
98
+ "/apps/server search SERPER_API_KEY"
99
+ "/apps/server infra REDIS_URL"
100
+ "/apps/web ai SYNTHETIC_AI_API_KEY"
101
+ "/packages/db core CORE_DATABASE_URL"
102
+ "/packages/db auth AUTH_DATABASE_URL"
103
+ "/packages/db pipeline PIPELINE_DATABASE_URL"
104
+ "/packages/db knowledge-base KB_DATABASE_URL"
105
+ "/packages/db knowledge-base KB_BUCKET_ENDPOINT"
106
+ "/packages/db knowledge-base KB_BUCKET_NAME"
107
+ "/packages/db knowledge-base KB_BUCKET_ACCESS_KEY_ID"
108
+ "/packages/db knowledge-base KB_BUCKET_SECRET_ACCESS_KEY"
109
+ "/packages/db knowledge-base KB_BUCKET_REGION"
110
+ )
111
+
112
+ # NOTE on app-native vars: deliberately NOT listed here. PORT, FRONTEND_DOMAIN,
113
+ # NEXT_PUBLIC_*, langfuse keys, etc. are set directly as real values in their app
114
+ # folder (by the user), never referenced. The skill prints them as
115
+ # "app-native — set directly."
@@ -0,0 +1,89 @@
1
+ #!/usr/bin/env bash
2
+ #
3
+ # infisical-sync.sh — create/maintain a domain-grouped /shared secret structure
4
+ # in Infisical, with each app/package folder holding ENVIRONMENT-AWARE references.
5
+ #
6
+ # SAFE BY DEFAULT: prints the plan, changes nothing unless APPLY=1.
7
+ # Idempotent. Never prints secret values. Never overwrites existing values.
8
+ # Runs across every environment in ENVIRONMENTS.
9
+ #
10
+ # APPLY=0 ./scripts/infisical-sync.sh # dry run (default)
11
+ # APPLY=1 ./scripts/infisical-sync.sh # apply to all environments
12
+ #
13
+ # Requires: `infisical login`, a committed .infisical.json in the cwd (run from a
14
+ # dir that has one, e.g. apps/server), and scripts/infisical-map.sh alongside.
15
+ #
16
+ # REFERENCE SYNTAX (the only form that resolves):
17
+ # ${<env-slug>.shared.<domain>.<KEY>} e.g. ${dev.shared.core.CORE_DATABASE_URL}
18
+ # The slug MUST match the environment the reference lives in.
19
+
20
+ set -uo pipefail
21
+ APPLY="${APPLY:-0}"
22
+ PLACEHOLDER="${PLACEHOLDER:-REPLACE_ME}"
23
+
24
+ HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
25
+ # shellcheck source=/dev/null
26
+ source "$HERE/infisical-map.sh" # defines: ENVIRONMENTS, DOMAINS, APP_PATHS,
27
+ # SHARED_MAP, REF_MAP. See infisical-map.sh.
28
+
29
+ plan() { printf ' %s\n' "$*"; }
30
+ banner() { printf '\n=== %s ===\n' "$*"; }
31
+
32
+ # REF <domain> <KEY> -> ${<ENV>.shared.<domain>.<KEY>} (ENV is the current env slug)
33
+ REF() { printf '${%s.shared.%s.%s}' "$ENV" "$1" "$2"; }
34
+
35
+ mkfolder() { # parentPath name
36
+ plan "folder $1/$2"
37
+ [ "$APPLY" = 1 ] && infisical secrets folders create \
38
+ --env "$ENV" --path "$1" --name "$2" --silent >/dev/null 2>&1 || true
39
+ }
40
+
41
+ # stub <domain> <KEY> [defaultValue]
42
+ # Sets /shared/<domain>/<KEY> to a placeholder ONLY if it has no value yet.
43
+ stub() {
44
+ local d="$1" k="$2" v="${3:-$PLACEHOLDER}" cur
45
+ cur="$(infisical secrets get "$k" --env "$ENV" --path "/shared/$d" --plain --silent 2>/dev/null)" || cur=""
46
+ if [ -n "$cur" ]; then plan "keep /shared/$d/$k (already set)"; return; fi
47
+ plan "stub /shared/$d/$k = $v"
48
+ [ "$APPLY" = 1 ] && infisical secrets set "$k=$v" --env "$ENV" --path "/shared/$d" --silent >/dev/null 2>&1 || true
49
+ }
50
+
51
+ # link <appPath> <domain> <KEY> -> sets KEY=${ENV.shared.domain.KEY} in the app folder
52
+ link() {
53
+ local app="$1" d="$2" k="$3"
54
+ plan "ref ${app}/${k} = $(REF "$d" "$k")"
55
+ [ "$APPLY" = 1 ] && infisical secrets set "${k}=$(REF "$d" "$k")" \
56
+ --env "$ENV" --path "$app" --silent >/dev/null 2>&1 || true
57
+ }
58
+
59
+ printf 'Infisical sync — APPLY=%s%s\n' "$APPLY" \
60
+ "$([ "$APPLY" = 1 ] && echo ' (LIVE)' || echo ' (dry run — no changes)')"
61
+
62
+ for ENV in "${ENVIRONMENTS[@]}"; do
63
+ banner "ENVIRONMENT: $ENV"
64
+
65
+ banner "1. Folders ($ENV)"
66
+ mkfolder / shared
67
+ for d in "${DOMAINS[@]}"; do mkfolder /shared "$d"; done
68
+ for app in "${APP_PATHS[@]}"; do
69
+ # ensure app/package folders exist (apps/server, apps/web, packages/db, ...)
70
+ parent="$(dirname "$app")"; name="$(basename "$app")"
71
+ [ "$parent" = "/" ] || mkfolder "$parent" "$name"
72
+ done
73
+
74
+ banner "2. Shared placeholders ($ENV)"
75
+ # SHARED_MAP entries look like "domain KEY [optionalDefault]"
76
+ for entry in "${SHARED_MAP[@]}"; do
77
+ # shellcheck disable=SC2086
78
+ stub $entry
79
+ done
80
+
81
+ banner "3. App references ($ENV)"
82
+ # REF_MAP entries look like "appPath domain KEY"
83
+ for entry in "${REF_MAP[@]}"; do
84
+ # shellcheck disable=SC2086
85
+ link $entry
86
+ done
87
+ done
88
+
89
+ printf '\nDone (%s).\n' "$([ "$APPLY" = 1 ] && echo applied || echo 'dry run — re-run with APPLY=1')"
package/dist/index.js CHANGED
@@ -6,7 +6,7 @@ import pc4 from "picocolors";
6
6
 
7
7
  // src/constants.ts
8
8
  var API_URL = process.env["WITHMATA_API_URL"] || "https://blueprints.withmata.dev";
9
- var VERSION = "0.7.0";
9
+ var VERSION = "3.0.0";
10
10
 
11
11
  // src/lib/install-state.ts
12
12
  import { existsSync, readdirSync, lstatSync, readlinkSync } from "fs";
@@ -269,7 +269,7 @@ function isBlueprintsRepo(dir) {
269
269
  function discoverBlueprints(blueprintsDir) {
270
270
  const ids = [];
271
271
  if (!existsSync3(blueprintsDir)) return ids;
272
- const tiers = ["discovery", "foundation", "features"];
272
+ const tiers = ["discovery", "foundation", "features", "thinking"];
273
273
  for (const tier of tiers) {
274
274
  const tierDir = join3(blueprintsDir, tier);
275
275
  if (!existsSync3(tierDir)) continue;
@@ -483,6 +483,15 @@ var SKILL_GROUPS = [
483
483
  description: "Check project health and find available upgrades"
484
484
  }
485
485
  ]
486
+ },
487
+ {
488
+ title: "Think",
489
+ skills: [
490
+ {
491
+ command: "/reflect",
492
+ description: "Run a structured thinking-partner session on any personal topic"
493
+ }
494
+ ]
486
495
  }
487
496
  ];
488
497
  function showSummary({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@withmata/blueprints",
3
- "version": "0.7.0",
3
+ "version": "3.0.0",
4
4
  "description": "Set up AI-powered project blueprints for Claude Code, OpenCode, and Cursor",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,17 +0,0 @@
1
- # =============================================================================
2
- # {{APP_NAME}} — Environment Variables
3
- # =============================================================================
4
- # Copy this file to .env.local and fill in the values.
5
- # Feature blueprints will add their own variables — check their BLUEPRINT.md.
6
-
7
- # -----------------------------------------------------------------------------
8
- # App
9
- # -----------------------------------------------------------------------------
10
-
11
- # Frontend URL (used for redirects, CORS, and metadata)
12
- # Local: http://localhost:3000 | Production: https://yourdomain.com
13
- NEXT_PUBLIC_FRONTEND_URL=http://localhost:3000
14
-
15
- # CONFIGURE: Feature blueprints add variables here:
16
- # - /scaffold-auth adds: BETTER_AUTH_URL, BETTER_AUTH_SECRET, GOOGLE_CLIENT_ID, etc.
17
- # - /scaffold-db adds: DATABASE_URL (in packages/db/.env for monorepos)
@@ -1,14 +0,0 @@
1
- # =============================================================================
2
- # {{APP_NAME}} Server — Environment Variables
3
- # =============================================================================
4
- # Copy this file to .env.local and fill in the values.
5
-
6
- # -----------------------------------------------------------------------------
7
- # Server Configuration
8
- # -----------------------------------------------------------------------------
9
-
10
- # Port the server listens on
11
- # Local: 4000 | Production: Set by hosting platform
12
- PORT=4000
13
-
14
- # CONFIGURE: Add server-specific variables below