@percepta/create 3.6.2 → 3.6.3

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 (82) hide show
  1. package/README.md +37 -6
  2. package/dist/{git-ops-C2CIjuce.js → git-ops-BD7JNnal.js} +1 -1
  3. package/dist/{git-ops-C2CIjuce.js.map → git-ops-BD7JNnal.js.map} +1 -1
  4. package/dist/github-RCIMUq70.js +131 -0
  5. package/dist/github-RCIMUq70.js.map +1 -0
  6. package/dist/index.js +63 -122
  7. package/dist/index.js.map +1 -1
  8. package/dist/{init-sI9aIrkU.js → init-COp0nGdk.js} +4 -2
  9. package/dist/{init-sI9aIrkU.js.map → init-COp0nGdk.js.map} +1 -1
  10. package/dist/manifest-CqIDnbgs.js +58 -0
  11. package/dist/manifest-CqIDnbgs.js.map +1 -0
  12. package/dist/register-app-C7ZBpAaZ.js +103 -0
  13. package/dist/register-app-C7ZBpAaZ.js.map +1 -0
  14. package/dist/register-os-blueprint-DGjBUZYa.js +90 -0
  15. package/dist/register-os-blueprint-DGjBUZYa.js.map +1 -0
  16. package/dist/{status-CKe4aKso.js → status-BXYaQ4a2.js} +3 -3
  17. package/dist/{status-CKe4aKso.js.map → status-BXYaQ4a2.js.map} +1 -1
  18. package/dist/{sync-D1vkoofl.js → sync-BayU4w1j.js} +3 -3
  19. package/dist/{sync-D1vkoofl.js.map → sync-BayU4w1j.js.map} +1 -1
  20. package/dist/template-versions-CEIP9vhl.js +35 -0
  21. package/dist/template-versions-CEIP9vhl.js.map +1 -0
  22. package/dist/{upstream-gUHLWSR1.js → upstream-CZEzLrS4.js} +3 -3
  23. package/dist/{upstream-gUHLWSR1.js.map → upstream-CZEzLrS4.js.map} +1 -1
  24. package/dist/validate-dssldJAj.js +14 -0
  25. package/dist/validate-dssldJAj.js.map +1 -0
  26. package/package.json +1 -1
  27. package/template-versions.json +2 -2
  28. package/templates/infra/os.blueprint.yaml.template +138 -0
  29. package/templates/monorepo/README.md +41 -3
  30. package/templates/monorepo/auth/README.md +6 -3
  31. package/templates/monorepo/auth/package.json +2 -4
  32. package/templates/monorepo/auth/src/config/database.ts +1 -1
  33. package/templates/{webapp → monorepo}/docker-compose.yml +2 -2
  34. package/templates/monorepo/package.json.template +5 -2
  35. package/templates/monorepo/scripts/setup-local-databases.mjs +183 -0
  36. package/templates/webapp/AGENTS.md +13 -20
  37. package/templates/webapp/README.md +32 -36
  38. package/templates/webapp/agent-skills/database.md +21 -21
  39. package/templates/webapp/agent-skills/langfuse.md +7 -7
  40. package/templates/webapp/agent-skills/llm.md +4 -2
  41. package/templates/webapp/agent-skills/oneshot.md +7 -6
  42. package/templates/webapp/agent-skills/ryvn.md +12 -16
  43. package/templates/webapp/deploy/README.md +10 -51
  44. package/templates/webapp/drizzle.config.ts +2 -23
  45. package/templates/webapp/env.example.template +8 -14
  46. package/templates/webapp/package.json.template +5 -12
  47. package/templates/webapp/scripts/start.sh +12 -16
  48. package/templates/webapp/src/config/getEnvConfig.ts +4 -10
  49. package/templates/webapp/src/drizzle/db.ts +6 -21
  50. package/templates/webapp/src/startup-checks.ts +28 -7
  51. package/templates/monorepo/auth/scripts/setup-database.ts +0 -11
  52. package/templates/webapp/.github/workflows/__APP_NAME__-terraform-ryvn-release.yaml +0 -92
  53. package/templates/webapp/agent-skills/deploy.md +0 -92
  54. package/templates/webapp/deploy/ryvn/__APP_NAME__-terraform.service.yaml +0 -10
  55. package/templates/webapp/deploy/ryvn/environments/percepta-test/installations/__APP_NAME__-terraform.env.percepta-test.serviceinstallation.yaml +0 -11
  56. package/templates/webapp/deploy/ryvn/environments/percepta-test/installations/__APP_NAME__.env.percepta-test.serviceinstallation.yaml +0 -154
  57. package/templates/webapp/terraform/README.md +0 -147
  58. package/templates/webapp/terraform/deploy.sh +0 -97
  59. package/templates/webapp/terraform/main.tf +0 -101
  60. package/templates/webapp/terraform/modules/cloudtrail/main.tf +0 -27
  61. package/templates/webapp/terraform/modules/cloudtrail/outputs.tf +0 -10
  62. package/templates/webapp/terraform/modules/cloudtrail/variables.tf +0 -15
  63. package/templates/webapp/terraform/modules/networking/main.tf +0 -118
  64. package/templates/webapp/terraform/modules/networking/outputs.tf +0 -38
  65. package/templates/webapp/terraform/modules/networking/variables.tf +0 -24
  66. package/templates/webapp/terraform/modules/rds/main.tf +0 -227
  67. package/templates/webapp/terraform/modules/rds/outputs.tf +0 -73
  68. package/templates/webapp/terraform/modules/rds/variables.tf +0 -61
  69. package/templates/webapp/terraform/modules/s3-logging/main.tf +0 -148
  70. package/templates/webapp/terraform/modules/s3-logging/outputs.tf +0 -10
  71. package/templates/webapp/terraform/modules/s3-logging/variables.tf +0 -16
  72. package/templates/webapp/terraform/modules/secrets/main.tf +0 -39
  73. package/templates/webapp/terraform/modules/secrets/outputs.tf +0 -9
  74. package/templates/webapp/terraform/modules/secrets/variables.tf +0 -51
  75. package/templates/webapp/terraform/outputs.tf +0 -102
  76. package/templates/webapp/terraform/providers.tf +0 -32
  77. package/templates/webapp/terraform/schema/main.tf +0 -4
  78. package/templates/webapp/terraform/schema/outputs.tf +0 -9
  79. package/templates/webapp/terraform/schema/variables.tf +0 -19
  80. package/templates/webapp/terraform/schema/versions.tf +0 -38
  81. package/templates/webapp/terraform/terraform.tfvars.example +0 -65
  82. package/templates/webapp/terraform/variables.tf +0 -129
@@ -11,7 +11,7 @@ pnpm install
11
11
  # Run development mode for all packages
12
12
  pnpm dev
13
13
 
14
- # Set up local services, access-control topology, databases, and seed users
14
+ # Set up root local services, databases, access-control topology, and seed users
15
15
  pnpm run setup
16
16
 
17
17
  # Add another Mosaic package using this workspace's pinned generator
@@ -35,10 +35,23 @@ pnpm lint
35
35
  ```
36
36
  access/ # Customer-level SpiceDB fixtures and generated merge artifacts
37
37
  auth/ # Shared Better Auth users/groups package for this customer
38
+ docker-compose.yml # Root-owned local PostgreSQL and SpiceDB services
39
+ scripts/ # Root-owned local infrastructure helpers
38
40
  packages/
39
41
  └── your-package/ # Application and library packages
40
42
  ```
41
43
 
44
+ ## Local Infrastructure
45
+
46
+ Local development infrastructure is owned by the monorepo root. `pnpm run setup`
47
+ starts the root Docker Compose services, creates the shared `auth` database,
48
+ discovers app `DATABASE_URL`s from `packages/*/.env.local`, creates those app
49
+ databases, runs auth and app migrations, and seeds local users/grants.
50
+
51
+ When you add another webapp with `pnpm mosaic add webapp my-app`, the generated
52
+ app gets its own local `DATABASE_URL`. Re-running `pnpm run setup` creates that
53
+ database before running migrations.
54
+
42
55
  ## Access Control
43
56
 
44
57
  Application builders define app-local Zed schemas in each package's
@@ -56,7 +69,7 @@ PR CI merges the customer schema and runs static schema/manifest validation.
56
69
  `access:apply` is reserved for trusted deploy jobs and should run once per
57
70
  target environment with that environment's SpiceDB credentials.
58
71
 
59
- For local development, `pnpm run setup` seeds the generated app's default dev
72
+ For local development, `pnpm run setup` seeds each generated app's default dev
60
73
  users and access grants. For additional local grants, copy the example fixture
61
74
  files in `access/`, fill in customer-global user/group IDs from `auth/`, then
62
75
  run:
@@ -83,4 +96,29 @@ pnpm mosaic add webapp my-app
83
96
  pnpm mosaic add library my-lib
84
97
  ```
85
98
 
86
- The compatibility metadata lives in `.mosaic-workspace.json`.
99
+ The customer slug and compatibility metadata live in `.mosaic-workspace.json`.
100
+
101
+ ## Registering the customer OS blueprint
102
+
103
+ To register this customer monorepo's OS blueprint in `Percepta-Core/infra`,
104
+ run:
105
+
106
+ ```bash
107
+ pnpm mosaic infra register-os-blueprint
108
+ ```
109
+
110
+ The command reads the customer slug from `.mosaic-workspace.json`, opens or
111
+ updates an infra PR that writes
112
+ `ryvn/definitions/<customer>/blueprints/<customer>-os.blueprint.yaml`, and does
113
+ not create environment installations. It authenticates with `GITHUB_TOKEN`,
114
+ `GH_TOKEN`, or the GitHub CLI's `gh auth token`.
115
+
116
+ After adding a webapp, register its app database in the customer OS blueprint:
117
+
118
+ ```bash
119
+ pnpm mosaic infra register-app my-app
120
+ ```
121
+
122
+ This opens or updates an infra PR that adds `my-app: {}` to the customer OS
123
+ blueprint's `app_databases` default. Merge the OS blueprint PR before
124
+ registering apps.
@@ -22,6 +22,9 @@ lands.
22
22
 
23
23
  ## Database
24
24
 
25
- By default this package uses the customer monorepo database name generated at
26
- scaffold time. Override with `AUTH_DATABASE_NAME` or `AUTH_DATABASE_URL` when
27
- the shared auth database lives somewhere else.
25
+ By default this package uses a shared `auth` database with local PostgreSQL
26
+ defaults. Remote deployments should inject `AUTH_DATABASE_URL` from the
27
+ monorepo auth database Secret.
28
+
29
+ Do not use app `DATABASE_URL` for this package. That belongs to each webapp's
30
+ own database.
@@ -13,12 +13,10 @@
13
13
  "scripts": {
14
14
  "build": "tsc -p tsconfig.json",
15
15
  "db:generate": "drizzle-kit generate",
16
- "db:migrate": "drizzle-kit migrate",
17
- "db:setup": "tsx ./scripts/setup-database.ts",
18
- "db:setup-and-migrate": "pnpm db:setup && pnpm db:migrate"
16
+ "db:migrate": "drizzle-kit migrate"
19
17
  },
20
18
  "dependencies": {
21
- "@percepta/auth": "0.1.3",
19
+ "@percepta/auth": "0.1.4",
22
20
  "drizzle-orm": "^0.45.2"
23
21
  },
24
22
  "devDependencies": {
@@ -7,7 +7,7 @@ import {
7
7
  export type { AuthDatabaseConfig } from "@percepta/auth";
8
8
 
9
9
  export function getAuthDatabaseConfig(): AuthDatabaseConfig {
10
- return readAuthDatabaseConfig({ defaultDatabaseName: "__DB_NAME__" });
10
+ return readAuthDatabaseConfig({ defaultDatabaseName: "auth" });
11
11
  }
12
12
 
13
13
  export function getAuthDatabaseConnectionString(): string {
@@ -22,11 +22,11 @@ services:
22
22
  environment:
23
23
  POSTGRES_USER: postgres
24
24
  POSTGRES_PASSWORD: postgres
25
- POSTGRES_DB: __DB_NAME__
25
+ POSTGRES_DB: postgres
26
26
  volumes:
27
27
  - postgres_data:/var/lib/postgresql/data
28
28
  healthcheck:
29
- test: ["CMD-SHELL", "pg_isready -U postgres -d __DB_NAME__"]
29
+ test: ["CMD-SHELL", "pg_isready -U postgres -d postgres"]
30
30
  interval: 5s
31
31
  timeout: 5s
32
32
  retries: 5
@@ -6,7 +6,10 @@
6
6
  "scripts": {
7
7
  "preinstall": "npx only-allow pnpm",
8
8
  "mosaic": "pnpm dlx __CREATE_PACKAGE__@__CREATE_VERSION__",
9
- "setup": "pnpm -r --filter './packages/*' --if-present run docker:up && pnpm run access:apply-local && pnpm run auth:db:setup-and-migrate && pnpm -r --filter './packages/*' --if-present run db:setup-and-migrate && pnpm -r --filter './packages/*' --if-present run db:seed",
9
+ "setup": "pnpm run docker:up && pnpm run db:setup-local && pnpm run auth:db:migrate && pnpm run access:apply-local && pnpm -r --filter './packages/*' --if-present run db:migrate && pnpm -r --filter './packages/*' --if-present run db:seed",
10
+ "docker:up": "docker compose up -d --wait",
11
+ "docker:down": "docker compose down",
12
+ "db:setup-local": "node scripts/setup-local-databases.mjs",
10
13
  "dev": "pnpm -r --parallel --if-present run dev",
11
14
  "build": "pnpm -r --if-present run build",
12
15
  "clean": "pnpm -r --if-present run clean",
@@ -22,7 +25,7 @@
22
25
  "access:bootstrap-customer-admin": "percepta-access-control bootstrap-customer-admin",
23
26
  "access:apply-bootstrap-grants": "percepta-access-control apply-bootstrap-grants --fixture access/bootstrap-grants.yaml",
24
27
  "access:reconcile": "percepta-access-control reconcile --input access/reconcile.yaml",
25
- "auth:db:setup-and-migrate": "pnpm --dir auth run db:setup-and-migrate"
28
+ "auth:db:migrate": "pnpm --dir auth run db:migrate"
26
29
  },
27
30
  "engines": {
28
31
  "node": ">=20",
@@ -0,0 +1,183 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { execFileSync } from "node:child_process";
4
+ import { existsSync, readFileSync, readdirSync } from "node:fs";
5
+ import path from "node:path";
6
+ import { fileURLToPath } from "node:url";
7
+
8
+ const LOCAL_POSTGRES_HOSTS = new Set(["localhost", "127.0.0.1", "::1"]);
9
+ const LOCAL_POSTGRES_PORT = "5434";
10
+ const POSTGRES_SERVICE = "postgres";
11
+ const POSTGRES_USER = "postgres";
12
+ const ROOT_DIR = path.resolve(
13
+ path.dirname(fileURLToPath(import.meta.url)),
14
+ "..",
15
+ );
16
+ const PACKAGES_DIR = path.join(ROOT_DIR, "packages");
17
+
18
+ const databases = new Set(["auth"]);
19
+
20
+ for (const packageDir of listPackageDirs()) {
21
+ const database = readPackageDatabaseName(packageDir);
22
+ if (database != null) {
23
+ databases.add(database);
24
+ }
25
+ }
26
+
27
+ for (const database of [...databases].sort()) {
28
+ ensureDatabase(database);
29
+ }
30
+
31
+ function listPackageDirs() {
32
+ if (!existsSync(PACKAGES_DIR)) return [];
33
+
34
+ return readdirSync(PACKAGES_DIR, { withFileTypes: true })
35
+ .filter((entry) => entry.isDirectory())
36
+ .map((entry) => packageDirFor(entry.name));
37
+ }
38
+
39
+ function packageDirFor(packageName) {
40
+ if (
41
+ packageName === "." ||
42
+ packageName === ".." ||
43
+ packageName.includes("/") ||
44
+ packageName.includes("\\")
45
+ ) {
46
+ throw new Error(`Unexpected package directory name: ${packageName}`);
47
+ }
48
+
49
+ return path.join(PACKAGES_DIR, packageName);
50
+ }
51
+
52
+ function readPackageDatabaseName(packageDir) {
53
+ const env = readPackageEnvFile(packageDir);
54
+ const databaseUrl = env.DATABASE_URL;
55
+ if (databaseUrl == null || databaseUrl.length === 0) return null;
56
+
57
+ let url;
58
+ try {
59
+ url = new URL(databaseUrl);
60
+ } catch {
61
+ throw new Error(
62
+ `Invalid DATABASE_URL in ${path.relative(ROOT_DIR, packageDir)}/.env.local`,
63
+ );
64
+ }
65
+
66
+ if (url.protocol !== "postgres:" && url.protocol !== "postgresql:") {
67
+ throw new Error(
68
+ `DATABASE_URL in ${path.relative(ROOT_DIR, packageDir)}/.env.local must use postgres or postgresql.`,
69
+ );
70
+ }
71
+
72
+ const port = url.port || "5432";
73
+ if (!LOCAL_POSTGRES_HOSTS.has(url.hostname) || port !== LOCAL_POSTGRES_PORT) {
74
+ console.log(
75
+ `Skipping non-local app database for ${path.relative(ROOT_DIR, packageDir)}.`,
76
+ );
77
+ return null;
78
+ }
79
+
80
+ const database = decodeURIComponent(url.pathname.replace(/^\/+/, ""));
81
+ if (database.length === 0) {
82
+ throw new Error(
83
+ `DATABASE_URL in ${path.relative(ROOT_DIR, packageDir)}/.env.local must include a database name.`,
84
+ );
85
+ }
86
+
87
+ return database;
88
+ }
89
+
90
+ function readPackageEnvFile(packageDir) {
91
+ const safePackageDir = assertPackageDir(packageDir);
92
+ const envPath = path.join(safePackageDir, ".env.local");
93
+ if (!existsSync(envPath)) return {};
94
+
95
+ const env = {};
96
+ const content = readFileSync(envPath, "utf8");
97
+ for (const rawLine of content.split(/\r?\n/)) {
98
+ const line = rawLine.trim();
99
+ if (!line || line.startsWith("#")) continue;
100
+
101
+ const normalized = line.startsWith("export ") ? line.slice(7).trim() : line;
102
+ const separatorIndex = normalized.indexOf("=");
103
+ if (separatorIndex === -1) continue;
104
+
105
+ const key = normalized.slice(0, separatorIndex).trim();
106
+ const rawValue = normalized.slice(separatorIndex + 1).trim();
107
+ if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key)) continue;
108
+
109
+ env[key] = unquote(rawValue);
110
+ }
111
+
112
+ return env;
113
+ }
114
+
115
+ function assertPackageDir(packageDir) {
116
+ const resolvedPackageDir = path.resolve(packageDir);
117
+ const relative = path.relative(PACKAGES_DIR, resolvedPackageDir);
118
+
119
+ if (
120
+ relative.length === 0 ||
121
+ relative.startsWith("..") ||
122
+ path.isAbsolute(relative) ||
123
+ relative.includes(path.sep)
124
+ ) {
125
+ throw new Error(`Unexpected package directory: ${packageDir}`);
126
+ }
127
+
128
+ return resolvedPackageDir;
129
+ }
130
+
131
+ function unquote(value) {
132
+ if (
133
+ (value.startsWith('"') && value.endsWith('"')) ||
134
+ (value.startsWith("'") && value.endsWith("'"))
135
+ ) {
136
+ return value.slice(1, -1);
137
+ }
138
+
139
+ return value;
140
+ }
141
+
142
+ function ensureDatabase(database) {
143
+ const exists = execDockerCompose([
144
+ "exec",
145
+ "-T",
146
+ POSTGRES_SERVICE,
147
+ "psql",
148
+ "-U",
149
+ POSTGRES_USER,
150
+ "-d",
151
+ "postgres",
152
+ "-tAc",
153
+ `SELECT 1 FROM pg_database WHERE datname = ${quotePgLiteral(database)}`,
154
+ ]).trim();
155
+
156
+ if (exists === "1") {
157
+ console.log(`Database ${database} already exists.`);
158
+ return;
159
+ }
160
+
161
+ execDockerCompose([
162
+ "exec",
163
+ "-T",
164
+ POSTGRES_SERVICE,
165
+ "createdb",
166
+ "-U",
167
+ POSTGRES_USER,
168
+ database,
169
+ ]);
170
+ console.log(`Database ${database} created.`);
171
+ }
172
+
173
+ function execDockerCompose(args) {
174
+ return execFileSync("docker", ["compose", ...args], {
175
+ cwd: ROOT_DIR,
176
+ encoding: "utf8",
177
+ stdio: ["ignore", "pipe", "inherit"],
178
+ });
179
+ }
180
+
181
+ function quotePgLiteral(value) {
182
+ return `'${value.replaceAll("'", "''")}'`;
183
+ }
@@ -8,20 +8,18 @@ Next.js 15 full-stack application scaffolded from the Mosaic webapp template via
8
8
  - `pnpm build` — production build
9
9
  - `pnpm lint` — run ESLint
10
10
  - `pnpm test` — run Vitest tests
11
- - `pnpm docker:up` / `pnpm docker:down` — start PostgreSQL and SpiceDB, waiting for health, or stop them
12
11
  - `pnpm access:validate` — validate access schema and manifest
13
12
  - `pnpm access:apply-local` — apply merged customer access schema to local SpiceDB
14
- - `pnpm auth:db:setup-and-migrate` — setup and migrate the shared customer auth database
13
+ - `pnpm auth:db:migrate` — run shared customer auth database migrations
15
14
  - `pnpm inngest:dev` — start local Inngest dev server when working on background jobs
16
15
  - `pnpm db:generate` — generate Drizzle migrations
17
16
  - `pnpm db:migrate` — apply migrations
18
- - `pnpm db:setup-and-migrate` — create DB + migrate
19
- - `pnpm db:studio` — setup/migrate the database, then open Drizzle Studio
17
+ - `pnpm db:studio` — run migrations, then open Drizzle Studio
20
18
  - `pnpm db:seed` — seed shared-auth dev users and local grants for customer admin, app admin, app user, and app non-user personas (all use password)
21
19
 
22
20
  **Package manager**: Always use `pnpm`, never `npm` or `yarn`.
23
21
 
24
- Local development requires PostgreSQL and SpiceDB. Run Inngest locally when a workflow needs it. Do not run a local LGTM/Langfuse stack unless you are specifically debugging telemetry; those are wired by the Ryvn environment for deploys.
22
+ Local PostgreSQL and SpiceDB are owned by the monorepo root. Use `pnpm run setup` from this package or the root to start services, create local databases, run migrations, and seed users/grants. Run Inngest locally when a workflow needs it. Do not run a local LGTM/Langfuse stack unless you are specifically debugging telemetry; those are wired by the Ryvn environment for deploys.
25
23
  If local LLM calls are needed, `pnpm dev` loads shared provider keys from `~/.config/percepta/create.env` when it exists.
26
24
 
27
25
  ## Code Style
@@ -58,13 +56,9 @@ src/ # Application source
58
56
  │ └── observability/ # OpenTelemetry setup
59
57
  └── utils/ # Helpers (cn, pathEncryption, etc.)
60
58
 
61
- deploy/ # Infrastructure-as-code for Ryvn deployments
59
+ deploy/ # Optional release metadata
62
60
  └── ryvn/
63
- ├── __APP_NAME__.service.yaml # Ryvn web service
64
- ├── __APP_NAME__-terraform.service.yaml # Ryvn schema service
65
- └── environments/<env>/installations/
66
- ├── __APP_NAME__.env.<env>.serviceinstallation.yaml
67
- └── __APP_NAME__-terraform.env.<env>.serviceinstallation.yaml
61
+ └── __APP_NAME__.service.yaml # Ryvn web service
68
62
  ```
69
63
 
70
64
  ## @percepta Packages
@@ -200,8 +194,7 @@ Detailed how-to guides for each major stack component. Read the relevant guide w
200
194
  | LLM Observability (Langfuse) | [agent-skills/langfuse.md](agent-skills/langfuse.md) | App uses LLMs and needs trace/eval monitoring |
201
195
  | Database (Drizzle) | [agent-skills/database.md](agent-skills/database.md) | Adding tables, writing migrations, querying data |
202
196
  | Access Control (SpiceDB) | [agent-skills/access-control.md](agent-skills/access-control.md) | Adding Zed policy, app roles, permissioned resources, or group sync |
203
- | Deployment (Ryvn) | [agent-skills/ryvn.md](agent-skills/ryvn.md) | Ryvn overview and Percepta environment context |
204
- | Deploy to percepta-test | [agent-skills/deploy.md](agent-skills/deploy.md) | Step-by-step deploy using the pre-scaffolded `deploy/ryvn/` IaC files |
197
+ | Deployment (Ryvn) | [agent-skills/ryvn.md](agent-skills/ryvn.md) | Ryvn service release notes; environment installs live in infra |
205
198
  | Build App (Oneshot) | [agent-skills/oneshot.md](agent-skills/oneshot.md) | Building a complete app from requirements end-to-end |
206
199
 
207
200
  ## Key Patterns
@@ -221,14 +214,14 @@ Client-side usage via `src/lib/trpc.ts`.
221
214
 
222
215
  ### Authentication
223
216
 
224
- Better Auth is configured in the customer monorepo's shared `@__REPO_NAME__/auth` package. The app imports it through `src/lib/auth/` for local session validation.
217
+ Better Auth is configured in the customer monorepo's shared `@__REPO_NAME__/auth` package. The app imports it through `src/lib/auth/` for local session validation. `DATABASE_URL` is this app's database only; deployed auth should use `AUTH_DATABASE_URL` from the monorepo auth Secret.
225
218
 
226
219
  - **Server-side**: `auth.api.getSession({ headers: await headers() })` — get session in server components or tRPC context
227
220
  - **Client-side**: `authClient.useSession()` — React hook from `src/lib/auth-client.ts`
228
221
  - **Sign in**: `authClient.signIn.email({ email, password })` — client-side
229
222
  - **Sign out**: `authClient.signOut()` — client-side
230
223
  - **API route**: `src/app/api/auth/[...all]/route.ts` — Better Auth handler
231
- - **Env vars**: `BETTER_AUTH_SECRET` (required), `BETTER_AUTH_URL` (defaults to `http://localhost:3000`)
224
+ - **Env vars**: `BETTER_AUTH_SECRET` (required), `BETTER_AUTH_URL` (defaults to `http://localhost:3000`), `AUTH_DATABASE_URL` for deployed shared auth DB wiring
232
225
 
233
226
  ### Background Jobs
234
227
 
@@ -248,11 +241,11 @@ OpenTelemetry initialized in `src/instrumentation.ts`. Server traces and metrics
248
241
 
249
242
  ## Deployment
250
243
 
251
- To deploy this app to percepta-test, follow [agent-skills/deploy.md](agent-skills/deploy.md). The direct deploy helper treats percepta-test as an existing platform environment: it preflights the shared Postgres, Inngest, OTEL collector, and LGTM installations, then creates/updates this app's Ryvn services and installations.
252
-
253
- The release CI/CD workflows are already included under `.github/workflows/`.
254
-
255
- For Ryvn CLI operations, use the `/use-ryvn` skill.
244
+ Blueberry does not generate environment-specific deployments. Customer and
245
+ stack-specific infrastructure, app database registration, runtime services,
246
+ ingress, secrets, and service installations belong in the infra repo. The
247
+ release CI/CD workflow is included under `.github/workflows/` for stacks that
248
+ use Ryvn releases.
256
249
 
257
250
  ## Template Sync
258
251
 
@@ -1,6 +1,6 @@
1
1
  # __APP_TITLE__
2
2
 
3
- A production-ready Next.js application with authentication, database, logging, background jobs, and infrastructure as code.
3
+ A production-ready Next.js application with authentication, database, logging, and background jobs.
4
4
 
5
5
  Design theme: `__MOSAIC_DESIGN_THEME__`
6
6
 
@@ -14,7 +14,7 @@ Design theme: `__MOSAIC_DESIGN_THEME__`
14
14
  - **Background Jobs** with Inngest
15
15
  - **LLM Calls** with a provider-backed `LLMService`
16
16
  - **Observability** with OpenTelemetry, LGTM-compatible traces/metrics/logs, and Langfuse integration
17
- - **Infrastructure** with Terraform modules for AWS (RDS, S3, IAM)
17
+ - **Deployment-ready** Dockerfile and environment-neutral service metadata
18
18
  - **Type Safety** with TypeScript and Zod schemas
19
19
 
20
20
  ## Quick Start
@@ -29,9 +29,9 @@ Copy `.env.example` to `.env.local` and configure any app-specific environment v
29
29
  pnpm run setup
30
30
  ```
31
31
 
32
- This starts local PostgreSQL and SpiceDB, applies the local access schema,
33
- sets up the shared customer auth database, runs app migrations, and seeds dev
34
- users.
32
+ This delegates to the monorepo root. The root setup starts local PostgreSQL and
33
+ SpiceDB, creates the shared auth database and this app's database, applies the
34
+ local access schema, runs migrations, and seeds dev users.
35
35
 
36
36
  ### 3. Start Inngest When Using Background Jobs
37
37
 
@@ -49,7 +49,7 @@ pnpm dev
49
49
 
50
50
  Open [http://localhost:3000](http://localhost:3000) to see your app.
51
51
 
52
- OpenTelemetry, Faro, and Langfuse are optional in local development. Leave their env vars empty unless you are actively debugging telemetry; production deploys wire server traces, metrics, logs, and shared Langfuse demo credentials into the target Ryvn environment. General HTTP and database spans go to the OTEL/LGTM pipeline; Langfuse receives only AI SDK spans by default.
52
+ OpenTelemetry, Faro, and Langfuse are optional in local development. Leave their env vars empty unless you are actively debugging telemetry. Remote deployments should provide telemetry and Langfuse values through the target platform. General HTTP and database spans go to the OTEL/LGTM pipeline; Langfuse receives only AI SDK spans by default.
53
53
 
54
54
  If you need local LLM calls, set provider keys once in your shell profile or in `~/.config/percepta/create.env`:
55
55
 
@@ -88,17 +88,13 @@ src/
88
88
  | `pnpm build` | Build for production |
89
89
  | `pnpm start` | Start production server |
90
90
  | `pnpm lint` | Run ESLint |
91
- | `pnpm docker:up` | Start PostgreSQL and SpiceDB containers and wait until they are healthy |
92
- | `pnpm docker:down` | Stop PostgreSQL and SpiceDB containers |
93
91
  | `pnpm access:validate` | Validate the access manifest and schema |
94
92
  | `pnpm access:apply-local` | Apply the merged customer access schema to local SpiceDB |
95
- | `pnpm auth:db:setup-and-migrate` | Setup and migrate the shared customer auth database |
93
+ | `pnpm auth:db:migrate` | Run migrations for the shared customer auth database |
96
94
  | `pnpm inngest:dev` | Start the local Inngest dev server for this app |
97
95
  | `pnpm db:generate` | Generate Drizzle migrations |
98
96
  | `pnpm db:migrate` | Run database migrations |
99
- | `pnpm db:setup` | Create database and user |
100
- | `pnpm db:setup-and-migrate` | Setup and migrate database |
101
- | `pnpm db:studio` | Setup/migrate the database, then open Drizzle Studio |
97
+ | `pnpm db:studio` | Run migrations, then open Drizzle Studio |
102
98
  | `pnpm db:seed` | Seed default shared-auth dev users and local access grants |
103
99
  | `pnpm test:e2e:install` | Install the Chromium browser used by Playwright |
104
100
  | `pnpm test:e2e` | Run Playwright e2e tests after local setup |
@@ -153,15 +149,19 @@ logger.error(
153
149
 
154
150
  ## Authentication
155
151
 
156
- This app consumes the customer monorepo's shared [Better Auth](https://better-auth.com) package, `@__REPO_NAME__/auth`. The app still serves local development auth routes, but the users, sessions, accounts, groups, and group memberships live in the shared customer auth database.
152
+ This app consumes the customer monorepo's shared [Better Auth](https://better-auth.com) package, `@__REPO_NAME__/auth`. The app still serves local development auth routes, but the users, sessions, accounts, groups, and group memberships live in the shared customer auth database. Deployed apps should receive that shared database through `AUTH_DATABASE_URL` from the monorepo auth Secret; `DATABASE_URL` is reserved for this app's own database.
157
153
 
158
- Required environment variables:
154
+ Required auth environment variables:
159
155
 
160
156
  ```bash
161
157
  BETTER_AUTH_SECRET=generate-with-openssl-rand-base64-32
162
158
  BETTER_AUTH_URL=http://localhost:3000
163
159
  ```
164
160
 
161
+ Remote deployments should also set `AUTH_DATABASE_URL` from the shared auth
162
+ database Secret. Local development can omit it and use the root-created local
163
+ `auth` database.
164
+
165
165
  To create dev users:
166
166
  ```bash
167
167
  pnpm db:seed
@@ -184,17 +184,17 @@ App permissions are authored in `src/access/schema.zed`; `src/access/access.mani
184
184
  | `NODE_ENV` | Environment mode | `development` |
185
185
  | `APP_BASE_URL` | Base URL for the app | - |
186
186
 
187
- ### Database
187
+ ### App Database
188
188
 
189
189
  | Variable | Description | Default |
190
190
  |----------|-------------|---------|
191
- | `DATABASE_HOST` | PostgreSQL host | `localhost` |
192
- | `DATABASE_PORT` | PostgreSQL port | `5434` |
193
- | `DATABASE_USERNAME` | Database user | `postgres` |
194
- | `DATABASE_PASSWORD` | Database password | `postgres` |
195
- | `DATABASE_NAME` | Database name | `__DB_NAME__` |
196
- | `DATABASE_SCHEMA` | Optional Postgres schema/search path | - |
197
- | `DATABASE_USE_SSL` | Enable SSL | `false` |
191
+ | `DATABASE_URL` | App PostgreSQL connection URL | `postgresql://postgres:postgres@localhost:5434/__DB_NAME__` |
192
+
193
+ ### Shared Auth Database
194
+
195
+ | Variable | Description | Default |
196
+ |----------|-------------|---------|
197
+ | `AUTH_DATABASE_URL` | Shared auth database URL from the monorepo auth Secret | - |
198
198
 
199
199
  ### Security
200
200
 
@@ -231,13 +231,13 @@ node -e "console.log(require('crypto').randomBytes(16).toString('hex'))"
231
231
  | `LANGFUSE_PUBLIC_KEY` | Langfuse public key |
232
232
  | `LANGFUSE_SECRET_KEY` | Langfuse secret key |
233
233
 
234
- `deploy:percepta-test` sets the shared Langfuse URL and inherits the demo project keys from the `demos-commons` Ryvn variable group.
234
+ Set these values through the target deployment platform when Langfuse is enabled.
235
235
 
236
236
  ### LLM Providers
237
237
 
238
238
  | Variable | Description |
239
239
  |----------|-------------|
240
- | `ANTHROPIC_API_KEY` | Anthropic API key. Inherited from `demos-commons` for `percepta-test` deploys |
240
+ | `ANTHROPIC_API_KEY` | Anthropic API key |
241
241
  | `OPENAI_API_KEY` | OpenAI API key for local or non-demo deployments |
242
242
  | `LLM_PROVIDER` | Optional provider override: `anthropic` or `openai` |
243
243
  | `LLM_MODEL` | Optional model override |
@@ -254,7 +254,7 @@ const result = await LLMService.create().generateText({
254
254
  });
255
255
  ```
256
256
 
257
- For local development, `pnpm dev` also loads `~/.config/percepta/create.env` when that file exists. Production deploys do not use that local file; they inherit provider keys from the target Ryvn environment.
257
+ For local development, `pnpm dev` also loads `~/.config/percepta/create.env` when that file exists. Remote deployments do not use that local file; they should receive provider keys from the target platform.
258
258
 
259
259
  ### OpenTelemetry / LGTM
260
260
 
@@ -270,7 +270,7 @@ For local development, `pnpm dev` also loads `~/.config/percepta/create.env` whe
270
270
  | `OTEL_METRICS_EXPORTER` | Set to `otlp` to export server metrics |
271
271
  | `OTEL_METRIC_EXPORT_INTERVAL` | Metrics export interval in milliseconds |
272
272
 
273
- `deploy:percepta-test` configures these for the existing percepta-test OTEL collector and LGTM stack. Application logs are written to stdout and collected by the platform collector.
273
+ Configure these values through the target deployment platform. Application logs are written to stdout so the platform collector can collect them.
274
274
 
275
275
  ## Local AWS Development
276
276
 
@@ -284,17 +284,13 @@ This application uses the default AWS SDK credential provider chain:
284
284
 
285
285
  2. **AWS Credentials File**: Run `aws configure` or `aws sso login`
286
286
 
287
- ## Infrastructure (Terraform)
288
-
289
- The `terraform/` directory contains modules for AWS infrastructure:
290
-
291
- - **RDS**: PostgreSQL database
292
- - **S3**: File storage with logging
293
- - **Networking**: VPC endpoints
294
- - **Secrets**: AWS Secrets Manager
295
- - **IAM**: Roles and policies
287
+ ## Infrastructure
296
288
 
297
- See `terraform/README.md` for deployment instructions.
289
+ The app template does not own cloud infrastructure. The infra repo owns base
290
+ environment infrastructure, customer monorepo infrastructure, app database
291
+ registration, observability, and shared services. App deployments expect the
292
+ customer monorepo infrastructure blueprint to create an app-scoped Kubernetes
293
+ Secret named `__APP_NAME__-postgresql`.
298
294
 
299
295
  ## Learn More
300
296