@crossdelta/platform-sdk 0.8.4 → 0.8.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,137 +1,118 @@
1
1
  <p align="center">
2
- <img src="https://unpkg.com/@crossdelta/platform-sdk/logo.png" alt="pf" width="140" />
2
+ <img src="https://unpkg.com/@crossdelta/platform-sdk/logo.png" alt="pf" width="120" />
3
3
  </p>
4
4
 
5
5
  <h1 align="center">Platform SDK</h1>
6
6
 
7
7
  <p align="center">
8
- <strong>The platform toolkit for backend + infrastructure.</strong>
8
+ <code>@crossdelta/platform-sdk</code>
9
9
  </p>
10
10
 
11
11
  <p align="center">
12
- <code>pf</code> scaffolds real-world platforms: backend services, event-driven messaging, infrastructure all wired together and kept in sync.
12
+ <strong>Platform toolkit for event-driven microserviceskeeping code and infrastructure in lockstep.</strong><br />
13
+ Scaffold <a href="https://turbo.build/repo">Turborepo</a> workspaces, generate services from natural language,<br />
14
+ and deploy via <a href="https://www.pulumi.com">Pulumi</a> — DigitalOcean-first for speed and simplicity, providers replaceable by design.
13
15
  </p>
14
16
 
15
17
  <p align="center">
16
- <sub>Fontend SDK coming soon contract-based client models.</sub>
17
-
18
+ <em>Platform SDK (<code>pf</code>) is a platform opinion, not a generator.<br />
19
+ It encodes architectural decisions so teams don't have to rediscover them.</em>
18
20
  </p>
19
21
 
20
- <br />
22
+ <p align="center">
23
+ <em>Includes optional AI-assisted service generation (OpenAI / Anthropic).</em>
24
+ </p>
21
25
 
22
26
  <p align="center">
23
- <a href="https://www.npmjs.com/package/@crossdelta/platform-sdk"><img src="https://img.shields.io/npm/v/@crossdelta/platform-sdk.svg?style=for-the-badge&color=cb3837" alt="npm version" /></a>
24
- &nbsp;
25
- <img src="https://img.shields.io/badge/TypeScript-3178C6?style=for-the-badge&logo=typescript&logoColor=white" alt="TypeScript" />
26
- &nbsp;
27
- <img src="https://img.shields.io/badge/Bun-000000?style=for-the-badge&logo=bun&logoColor=white" alt="Bun" />
28
- &nbsp;
29
- <img src="https://img.shields.io/badge/License-MIT-22c55e?style=for-the-badge" alt="MIT License" />
27
+ <a href="https://www.npmjs.com/package/@crossdelta/platform-sdk"><img src="https://img.shields.io/npm/v/@crossdelta/platform-sdk.svg?style=flat-square" alt="npm version" /></a>
28
+ <img src="https://img.shields.io/badge/bun-%E2%9A%A1-f9f1e1?style=flat-square" alt="Bun" />
29
+ <img src="https://img.shields.io/badge/pulumi-ready-blueviolet?style=flat-square" alt="Pulumi" />
30
+ <img src="https://img.shields.io/badge/DigitalOcean-App_Platform_|_DOKS-0080FF?style=flat-square" alt="DigitalOcean" />
30
31
  </p>
31
32
 
32
33
  <p align="center">
33
34
  <img src="https://img.shields.io/badge/Hono-E36002?style=flat-square&logo=hono&logoColor=white" alt="Hono" />
34
35
  <img src="https://img.shields.io/badge/NestJS-E0234E?style=flat-square&logo=nestjs&logoColor=white" alt="NestJS" />
35
36
  <img src="https://img.shields.io/badge/NATS-27AAE1?style=flat-square&logo=natsdotio&logoColor=white" alt="NATS" />
36
- <img src="https://img.shields.io/badge/Pulumi-8A3391?style=flat-square&logo=pulumi&logoColor=white" alt="Pulumi" />
37
- <img src="https://img.shields.io/badge/Turborepo-EF4444?style=flat-square&logo=turborepo&logoColor=white" alt="Turborepo" />
38
- <img src="https://img.shields.io/badge/DigitalOcean-0080FF?style=flat-square&logo=digitalocean&logoColor=white" alt="DigitalOcean" />
39
37
  </p>
40
38
 
41
- <br />
39
+ ---
42
40
 
43
- <p align="center">
44
- <a href="#-quick-start">Quick Start</a> •
45
- <a href="#-5-minute-tutorial">Tutorial</a> •
46
- <a href="#-commands">Commands</a> •
47
- <a href="#-ai-assisted-generation">AI Generation</a> •
48
- <a href="#-deployment">Deployment</a>
49
- </p>
41
+ ## Who is this for?
50
42
 
51
- ---
43
+ - **Teams building event-driven backends** — NATS, CloudEvents, type-safe contracts
44
+ - **Startups & internal platforms** that want long-term consistency over short-term speed
45
+ - **Engineers tired of infra & app drift** — ports, env vars, deployments always in sync
52
46
 
53
- <br />
54
-
55
- ## 👋 Who is this for?
56
-
57
- <table>
58
- <tr>
59
- <td width="60">🏗️</td>
60
- <td><strong>Teams building full-stack platforms</strong> — services, events, apps that actually work together</td>
61
- </tr>
62
- <tr>
63
- <td>📡</td>
64
- <td><strong>Event-driven architectures</strong> — or teams ready to start using events</td>
65
- </tr>
66
- <tr>
67
- <td>😤</td>
68
- <td><strong>Engineers tired of drift</strong> — ports, env vars, and infra configs that never match</td>
69
- </tr>
70
- <tr>
71
- <td>🚀</td>
72
- <td><strong>Startups & platform teams</strong> — who want consistency without building everything from scratch</td>
73
- </tr>
74
- </table>
75
-
76
- > **You can start without understanding all the pieces.**
77
- > The depth reveals itself as you grow.
47
+ You can start without understanding all the pieces. The depth reveals itself as you grow.
78
48
 
79
49
  ---
80
50
 
81
- ## 🚀 Quick Start
51
+ ## Installation
82
52
 
83
53
  ```bash
84
- # With Bun
85
54
  bun add -g @crossdelta/platform-sdk
55
+ # or: npm install -g @crossdelta/platform-sdk
56
+ ```
86
57
 
87
- # With npm
88
- npm install -g @crossdelta/platform-sdk
58
+ <details>
59
+ <summary>Alternative: Auto-installer or run without installing</summary>
89
60
 
90
- # Or auto-install (macOS/Linux/WSL)
61
+ **Unix/macOS:**
62
+ ```bash
63
+ # Auto-installer (installs CLI globally, prompts to install Bun if missing)
91
64
  curl -fsSL https://unpkg.com/@crossdelta/platform-sdk@latest/install.sh | bash
92
65
  ```
93
66
 
94
- > **Windows users:** Use [WSL](https://learn.microsoft.com/en-us/windows/wsl/install) for the best experience.
67
+ **Windows:**
68
+ ```powershell
69
+ # Recommended: Use npm directly
70
+ npm install -g @crossdelta/platform-sdk
71
+
72
+ # Or use WSL (Windows Subsystem for Linux)
73
+ wsl bash -c "curl -fsSL https://unpkg.com/@crossdelta/platform-sdk@latest/install.sh | bash"
74
+ ```
75
+
76
+ **Run without installing:**
77
+ ```bash
78
+ bunx @crossdelta/platform-sdk new workspace my-platform
79
+ # or: npx @crossdelta/platform-sdk new workspace my-platform
80
+ ```
81
+
82
+ </details>
83
+
84
+ ---
85
+
86
+ ## Quick Start
95
87
 
96
- **Then create your first platform:**
97
88
  ```bash
98
89
  pf new workspace my-platform -y && cd my-platform
99
90
  pf dev
100
91
  ```
101
92
 
102
- <br />
103
-
104
- <table>
105
- <tr>
106
- <td>✅</td>
107
- <td>Turborepo monorepo with Biome linting</td>
108
- </tr>
109
- <tr>
110
- <td>✅</td>
111
- <td>NATS + JetStream for event-driven messaging</td>
112
- </tr>
113
- <tr>
114
- <td>✅</td>
115
- <td>Pulumi infrastructure ready for deployment</td>
116
- </tr>
117
- <tr>
118
- <td>✅</td>
119
- <td>Auto-generated <code>.env.local</code> with service ports</td>
120
- </tr>
121
- </table>
93
+ **What you get:**
94
+ - Turborepo monorepo with Biome linting
95
+ - NATS + JetStream for event-driven messaging
96
+ - Pulumi infrastructure (`infra/services/*.ts`)
97
+ - Auto-generated `.env.local` with service ports
98
+
99
+ > **Lockstep by design:**
100
+ > In `pf`, application code and infrastructure are generated from the same source of truth.
101
+ > Service metadata defines ports, env vars, event wiring, and Pulumi configs —
102
+ > eliminating drift between local development, CI, and production.
122
103
 
123
104
  ---
124
105
 
125
- ## 🎯 5-Minute Tutorial
106
+ ## 5-Minute Tutorial
126
107
 
127
- Add two services and wire an event between them:
108
+ After running Quick Start, add services and wire events:
128
109
 
129
110
  ```bash
130
- # 1. Create two services
111
+ # 1. Add two services
131
112
  pf new hono-micro services/orders
132
113
  pf new hono-micro services/notifications
133
114
 
134
- # 2. Wire an event from orders → notifications
115
+ # 2. Wire an event between them
135
116
  pf event add orders.created --service services/notifications
136
117
 
137
118
  # 3. Restart to pick up new services
@@ -141,194 +122,92 @@ pf dev
141
122
  pf event publish orders.created
142
123
  ```
143
124
 
144
- <br />
145
-
146
- <table>
147
- <tr>
148
- <th align="left">What just happened</th>
149
- </tr>
150
- <tr>
151
- <td>
152
- 📦 Two services with health checks and Pulumi configs<br />
153
- 📝 One event contract in <code>packages/contracts/</code><br />
154
- 🔗 One handler that auto-discovers the event<br />
155
- ✨ <strong>No manual wiring. No config drift. Everything is explicit.</strong>
156
- </td>
157
- </tr>
158
- </table>
159
-
160
- ---
161
-
162
- ## 💡 What problem does pf solve?
163
-
164
- <table>
165
- <tr>
166
- <th width="300">❌ The Problem</th>
167
- <th width="300">✅ With pf</th>
168
- </tr>
169
- <tr>
170
- <td>Application code lives here, infra config lives somewhere else</td>
171
- <td>Both generated from the same source of truth</td>
172
- </tr>
173
- <tr>
174
- <td>Ports, env vars, and event wiring drift over time</td>
175
- <td>Change a service → infra stays in sync</td>
176
- </tr>
177
- <tr>
178
- <td>New devs spend days setting up local environments</td>
179
- <td>Run <code>pf dev</code> → everything starts together</td>
180
- </tr>
181
- <tr>
182
- <td>Manual event subscriptions and handler registration</td>
183
- <td>Add an event → consumers are wired automatically</td>
184
- </tr>
185
- </table>
186
-
187
- ---
188
-
189
- ## 📋 Commands
190
-
191
- | Command | What it does |
192
- |:--------|:-------------|
193
- | `pf dev` | 🚀 Start NATS + all services in watch mode |
194
- | `pf new hono-micro <path>` | ⚡ Create a Hono service (lightweight, Bun-optimized) |
195
- | `pf new nest-micro <path>` | 🏢 Create a NestJS service (full-featured, decorators) |
196
- | `pf event add <type> --service <path>` | 🔗 Add contract + handler + wire stream |
197
- | `pf event list` | 📋 List available events and consumers |
198
- | `pf event publish <type>` | 📤 Publish mock event to NATS |
199
- | `pf test` | 🧪 Run tests across the monorepo |
200
- | `pf build` | 📦 Build all packages and services |
201
- | `pf lint` | ✨ Lint and format with Biome |
202
-
203
- > `pf` proxies all commands to Turborepo (works from any subdirectory).
125
+ That's it. Two services, one event contract, auto-discovered handlers, no manual wiring.
204
126
 
205
127
  ---
206
128
 
207
- ## 🤖 AI-Assisted Generation
129
+ ## 🧭 Commands & Options
208
130
 
209
- > **Optional but powerful.** Configure once, then use `--ai` with any generator.
131
+ ### Service generators
210
132
 
133
+ **Hono** (lightweight, Bun-optimized):
211
134
  ```bash
212
- pf setup --ai # OpenAI or Anthropic
213
-
214
- pf new hono-micro services/notifications --ai \
215
- -d "Send emails when orders are created"
135
+ pf new hono-micro services/orders
216
136
  ```
217
137
 
218
- AI generates service code, event handlers, use cases, and tests — **all following the same conventions you'd write by hand.**
219
-
220
- ---
221
-
222
- ## 🚢 Deployment
223
-
138
+ **NestJS** (full-featured, decorator-based):
224
139
  ```bash
225
- pf pulumi up --stack dev
140
+ pf new nest-micro services/orders
226
141
  ```
227
142
 
228
- **Pre-configured GitHub Actions included:**
229
-
230
- | Trigger | Action |
231
- |:--------|:-------|
232
- | PR to `main` | 🧪 Lint + test |
233
- | Push to `main` | 🚀 Build, push to GHCR, deploy |
234
- | Changes in `packages/` | 📦 Publish to npm |
235
-
236
- <details>
237
- <summary>🔐 Required secrets</summary>
238
-
239
- <br />
240
-
241
- | Secret | Description |
242
- |:-------|:------------|
243
- | `PULUMI_ACCESS_TOKEN` | [Pulumi Cloud token](https://app.pulumi.com/account/tokens) |
244
- | `DIGITALOCEAN_TOKEN` | [DO API token](https://cloud.digitalocean.com/account/api/tokens) |
245
- | `GHCR_TOKEN` | GitHub Container Registry PAT |
246
-
247
- </details>
248
-
249
- ---
250
-
251
- ## 📋 Requirements
252
-
253
- | Requirement | Notes |
254
- |:------------|:------|
255
- | **Bun** or Node.js ≥ 21 | Bun recommended for speed |
256
- | **[Pulumi CLI](https://www.pulumi.com/docs/install/)** | For infrastructure deployment |
257
- | **[Docker](https://www.docker.com/)** | For local NATS |
258
-
259
- ---
260
-
261
- ## 🔍 Under the Hood
262
-
263
- <details>
264
- <summary>📁 <b>Project structure</b></summary>
143
+ Both generate the service structure, Pulumi config in `infra/services/`, and wire everything automatically.
265
144
 
266
- <br />
145
+ ### AI-assisted generation (optional)
267
146
 
147
+ Configure once:
148
+ ```bash
149
+ pf setup --ai # OpenAI or Anthropic
268
150
  ```
269
- my-platform/
270
- ├── services/ # Your microservices
271
- │ ├── orders/
272
- │ └── notifications/
273
- ├── packages/
274
- │ └── contracts/ # Event contracts (single source of truth)
275
- ├── infra/
276
- │ └── services/ # Auto-generated Pulumi configs
277
- └── .env.local # Auto-generated from infra
151
+
152
+ Then use `--ai` with any generator:
153
+ ```bash
154
+ pf new hono-micro services/notifications --ai -d "Send emails on order events"
278
155
  ```
279
156
 
280
- **The key insight:** Services define their ports, env vars, and event subscriptions once. Everything else is derived.
157
+ AI generates: service code, event handlers, use cases, tests, and documentation.
158
+ All generated code follows the same conventions and can be written manually.
281
159
 
282
- </details>
160
+ ### Daily commands
283
161
 
284
- <details>
285
- <summary>🛠️ <b>Tech stack</b></summary>
286
-
287
- <br />
162
+ | Command | What it does |
163
+ |---------|--------------|
164
+ | `pf dev` | Start NATS + all services in watch mode |
165
+ | `pf test` | Run tests across the monorepo |
166
+ | `pf lint` | Lint and format with Biome |
167
+ | `pf build` | Build all packages and services |
168
+ | `pf event add <type> --service <path>` | Add contract, mock, handler + wire stream |
169
+ | `pf event list` | List available events and their consumers |
170
+ | `pf event publish <type>` | Publish mock event to NATS |
171
+ | `pf pulumi up` | Deploy infrastructure (runs in `infra/`) |
288
172
 
289
- | Layer | Technology |
290
- |:------|:-----------|
291
- | Monorepo | [Turborepo](https://turbo.build/repo) |
292
- | Services | [Hono](https://hono.dev) or [NestJS](https://nestjs.com) |
293
- | Messaging | [NATS](https://nats.io) + JetStream |
294
- | Events | [CloudEvents](https://cloudevents.io) + Zod validation |
295
- | Infrastructure | [Pulumi](https://pulumi.com) → DigitalOcean (extensible) |
296
- | Runtime | [Bun](https://bun.sh) |
173
+ `pf` proxies to Turborepo from any subdirectory.
297
174
 
298
- </details>
175
+ ### Configuration (advanced)
299
176
 
300
177
  <details>
301
- <summary>⚙️ <b>Workspace configuration</b></summary>
302
-
303
- <br />
178
+ <summary>Workspace configuration via <code>package.json</code></summary>
304
179
 
305
180
  ```json
306
181
  {
307
182
  "pf": {
308
- "registry": "my-org/my-platform",
183
+ "registry": "my-platform/platform",
309
184
  "commands": {
185
+ "pulumi": { "cwd": "infra" },
310
186
  "deploy": { "cwd": "infra", "command": "pulumi up --yes" }
311
187
  },
312
188
  "paths": {
313
189
  "services": { "path": "services", "watch": true },
190
+ "apps": { "path": "apps", "watch": true },
191
+ "packages": { "path": "packages", "watch": true },
314
192
  "contracts": { "path": "packages/contracts" }
315
193
  }
316
194
  }
317
195
  }
318
196
  ```
319
197
 
198
+ - `commands`: Custom shortcuts with working directory overrides
199
+ - `paths.<type>.watch`: Enable file watching in dev mode
200
+ - `paths.<type>.ignorePatterns`: Glob patterns to exclude
201
+
320
202
  </details>
321
203
 
322
204
  <details>
323
- <summary>🏗️ <b>Service infrastructure config</b></summary>
205
+ <summary>Infrastructure configuration via <code>infra/services/*.ts</code></summary>
324
206
 
325
- <br />
326
-
327
- ```typescript
328
- // infra/services/orders.ts
207
+ ```ts
329
208
  import { ports, type K8sServiceConfig } from '@crossdelta/infrastructure'
330
209
 
331
- const config: K8sServiceConfig = {
210
+ export const config: K8sServiceConfig = {
332
211
  name: 'orders',
333
212
  ports: ports().http(4001).build(),
334
213
  replicas: 1,
@@ -338,21 +217,102 @@ const config: K8sServiceConfig = {
338
217
  limits: { cpu: '150m', memory: '128Mi' },
339
218
  },
340
219
  }
341
-
342
- export default config
343
220
  ```
344
221
 
345
222
  See [@crossdelta/infrastructure](https://www.npmjs.com/package/@crossdelta/infrastructure) for full options.
346
223
 
347
224
  </details>
348
225
 
349
- <details>
350
- <summary>📡 <b>Event-driven architecture</b></summary>
226
+ ---
227
+
228
+ ## 🎯 Why This Exists
229
+
230
+ `pf` solves platform engineering problems:
231
+
232
+ - **Infrastructure drift** — Infra configs diverge from code in separate repos
233
+ - **Manual wiring** — Ports, env vars, service discovery configured by hand
234
+ - **Slow onboarding** — New devs spend days setting up local environments
235
+
236
+ **Solution:** Services auto-generate `infra/services/*.ts`, ports/env derived automatically, event handlers type-safe and auto-discovered.
237
+
238
+ **In short: docker-compose starts services — `pf` prevents systems from drifting apart over time.**
239
+
240
+ ---
241
+
242
+ ## 🧩 What pf Is / Is Not
243
+
244
+ **✅ pf is:**
245
+ - Opinionated platform toolkit for event-driven microservices
246
+ - Turborepo scaffolder with Pulumi IaC and NATS messaging built-in
247
+ - AI-assisted code generator from natural language descriptions
248
+ - Unified dev workflow — one command to run everything
249
+ - DigitalOcean-first for speed and simplicity — providers replaceable by design
250
+
251
+ **❌ pf is not:**
252
+ - Generic microservice generator — makes architectural choices for you
253
+ - Multi-cloud out of the box — DigitalOcean first, extensible to other providers
254
+ - Runtime manager — scaffolds code, you deploy with Pulumi
255
+
256
+ **Note:** `pf` runs nothing implicitly. No hidden daemons, no automatic deployments. You control when code runs and infrastructure deploys.
257
+
258
+ ---
259
+
260
+ ## 🏗️ Architecture
261
+
262
+ ```
263
+ my-platform/
264
+ ├── services/ # Microservices
265
+ │ ├── nats/ # NATS message broker (auto-scaffolded)
266
+ │ └── <service>/
267
+ │ └── src/
268
+ │ ├── events/ # Event handlers (*.event.ts)
269
+ │ ├── use-cases/ # Business logic (*.use-case.ts)
270
+ │ ├── types/ # Zod schemas
271
+ │ └── index.ts
272
+ ├── packages/
273
+ │ └── contracts/ # Event contracts (Schema Registry)
274
+ ├── infra/ # Pulumi Infrastructure-as-Code
275
+ │ └── services/ # Per-service K8s configs
276
+ └── .env.local # Auto-generated from infra
277
+ ```
278
+
279
+ ### Conventions
351
280
 
352
- <br />
281
+ | Location | Purpose |
282
+ |----------|---------|
283
+ | `services/*/src/events/*.event.ts` | Event handlers (auto-discovered) |
284
+ | `services/*/src/use-cases/*.use-case.ts` | Business logic (pure functions) |
285
+ | `packages/contracts/src/events/*.ts` | Event contracts (single source of truth) |
286
+ | `infra/services/*.ts` | Pulumi configs per service |
353
287
 
354
- **Contracts** live in `packages/contracts/` as the single source of truth:
288
+ > **Schema Registry pattern:**
289
+ > All event contracts live in `packages/contracts` as the single source of truth.
290
+ > Services import contracts instead of redefining schemas, ensuring
291
+ > type safety, compatibility, and explicit ownership across service boundaries.
355
292
 
293
+ ### Event-Driven Pattern
294
+
295
+ **Add events with one command:**
296
+ ```bash
297
+ # Creates contract, mock, handler, and wires the stream automatically
298
+ pf event add orders.created --service services/notifications
299
+ ```
300
+
301
+ This generates:
302
+ - `packages/contracts/src/events/orders-created.ts` (contract)
303
+ - `packages/contracts/src/events/orders-created.mock.json` (test data)
304
+ - `services/notifications/src/events/orders-created.event.ts` (handler)
305
+ - Adds stream to service `index.ts` if needed
306
+
307
+ **Test events locally:**
308
+ ```bash
309
+ pf event list # Show available events
310
+ pf event publish orders.created # Publish mock event to NATS
311
+ ```
312
+
313
+ **Manual pattern (if you prefer):**
314
+
315
+ **1. Define contract:**
356
316
  ```typescript
357
317
  // packages/contracts/src/events/orders-created.ts
358
318
  import { createContract } from '@crossdelta/cloudevents'
@@ -364,27 +324,25 @@ export const OrdersCreatedContract = createContract({
364
324
  })
365
325
  ```
366
326
 
367
- **Handlers** import contracts and delegate to use cases:
327
+ **2. Publish (Service A):**
328
+ ```typescript
329
+ // services/orders/src/use-cases/create-order.use-case.ts
330
+ await publish(OrdersCreatedContract, { orderId: '123', total: 99.99 })
331
+ ```
368
332
 
333
+ **3. Handle (Service B, auto-discovered):**
369
334
  ```typescript
370
335
  // services/notifications/src/events/orders-created.event.ts
371
- import { handleEvent } from '@crossdelta/cloudevents'
372
- import { OrdersCreatedContract } from '@my-org/contracts'
373
- import { sendOrderNotification } from '../use-cases/send-notification.use-case'
374
-
375
336
  export default handleEvent(OrdersCreatedContract, async (data) => {
376
337
  await sendOrderNotification(data)
377
338
  })
378
339
  ```
379
340
 
380
- Handlers are auto-discovered. No manual subscriptions.
381
-
382
- </details>
341
+ No manual subscriptions. Convention over configuration.
383
342
 
384
- <details>
385
- <summary>⚡ <b>NATS message broker</b></summary>
343
+ ---
386
344
 
387
- <br />
345
+ ## ⚡ NATS Message Broker
388
346
 
389
347
  Every workspace includes pre-configured NATS with JetStream:
390
348
 
@@ -392,39 +350,46 @@ Every workspace includes pre-configured NATS with JetStream:
392
350
  - Ports: `4222` (client), `8222` (monitoring)
393
351
  - Health check: `curl http://localhost:8222/healthz`
394
352
 
395
- </details>
353
+ See `services/nats/README.md` for details.
396
354
 
397
355
  ---
398
356
 
399
- ## 🧭 Philosophy
357
+ ## Deployment
358
+
359
+ ```bash
360
+ pulumi login && pulumi up --stack dev
361
+ ```
400
362
 
401
- <table>
402
- <tr>
403
- <td width="50%">
363
+ ### GitHub Actions (pre-configured)
404
364
 
405
- ### What pf is
365
+ | Workflow | Trigger | Purpose |
366
+ |----------|---------|---------|
367
+ | `lint-and-tests.yml` | PR to `main` | Lint + test |
368
+ | `build-and-deploy.yml` | Push to `main` | Build, push to GHCR, deploy |
369
+ | `publish-packages.yml` | Changes in `packages/` | Publish to npm |
406
370
 
407
- - Opinionated full-stack platform toolkit
408
- - Unified dev workflow — one command to run everything
409
- - Infrastructure that stays in sync with your code
371
+ <details>
372
+ <summary>Required secrets</summary>
410
373
 
411
- </td>
412
- <td width="50%">
374
+ | Secret | Description |
375
+ |--------|-------------|
376
+ | `PULUMI_ACCESS_TOKEN` | [Pulumi Cloud token](https://app.pulumi.com/account/tokens) |
377
+ | `DIGITALOCEAN_TOKEN` | [DO API token](https://cloud.digitalocean.com/account/api/tokens) |
378
+ | `GHCR_TOKEN` | GitHub Container Registry PAT |
379
+ | `NPM_TOKEN` | [npm access token](https://www.npmjs.com/settings/~/tokens) (only for `publish-packages.yml`) |
413
380
 
414
- ### ❌ What pf is not
381
+ </details>
415
382
 
416
- - Generic scaffolder — it makes choices for you
417
- - Multi-cloud out of the box — DigitalOcean first, extensible later
418
- - Magic — no hidden daemons, you control when things run
383
+ ---
419
384
 
420
- </td>
421
- </tr>
422
- </table>
385
+ ## 📚 Requirements
423
386
 
424
- > **`pf` makes architectural decisions so your team doesn't have to — without hiding how things work.**
387
+ - **Bun** (recommended) or Node.js 21
388
+ - **[Pulumi CLI](https://www.pulumi.com/docs/install/)** for deployment
389
+ - **[Docker](https://www.docker.com/)** for local NATS
425
390
 
426
391
  ---
427
392
 
428
- <p align="center">
429
- <strong>MIT © crossdelta</strong>
430
- </p>
393
+ ## 📄 License
394
+
395
+ MIT © Crossdelta