frontier-os-app-builder 1.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.
Files changed (55) hide show
  1. package/README.md +92 -0
  2. package/agents/fos-executor.md +460 -0
  3. package/agents/fos-plan-checker.md +386 -0
  4. package/agents/fos-planner.md +416 -0
  5. package/agents/fos-researcher.md +358 -0
  6. package/agents/fos-verifier.md +491 -0
  7. package/bin/fos-tools.cjs +794 -0
  8. package/bin/install.js +234 -0
  9. package/commands/fos/add-feature.md +29 -0
  10. package/commands/fos/discuss.md +31 -0
  11. package/commands/fos/execute.md +35 -0
  12. package/commands/fos/new-app.md +39 -0
  13. package/commands/fos/new-milestone.md +28 -0
  14. package/commands/fos/next.md +29 -0
  15. package/commands/fos/plan.md +37 -0
  16. package/commands/fos/ship.md +29 -0
  17. package/commands/fos/status.md +22 -0
  18. package/package.json +30 -0
  19. package/references/app-patterns.md +501 -0
  20. package/references/deployment.md +395 -0
  21. package/references/module-inference.md +349 -0
  22. package/references/sdk-surface.md +1622 -0
  23. package/references/verification-rules.md +404 -0
  24. package/templates/app/gitignore +25 -0
  25. package/templates/app/index.css +111 -0
  26. package/templates/app/index.html +19 -0
  27. package/templates/app/layout.tsx +45 -0
  28. package/templates/app/main-router.tsx +17 -0
  29. package/templates/app/main-simple.tsx +19 -0
  30. package/templates/app/package.json +36 -0
  31. package/templates/app/postcss.config.js +5 -0
  32. package/templates/app/router.tsx +22 -0
  33. package/templates/app/sdk-context.tsx +33 -0
  34. package/templates/app/test-setup.ts +19 -0
  35. package/templates/app/tsconfig.json +22 -0
  36. package/templates/app/vercel.json +127 -0
  37. package/templates/app/vite.config.ts +15 -0
  38. package/templates/state/context.md +248 -0
  39. package/templates/state/manifest.json +11 -0
  40. package/templates/state/plan.md +187 -0
  41. package/templates/state/project.md +118 -0
  42. package/templates/state/requirements.md +133 -0
  43. package/templates/state/roadmap.md +129 -0
  44. package/templates/state/state.md +131 -0
  45. package/templates/state/summary.md +273 -0
  46. package/workflows/add-feature.md +234 -0
  47. package/workflows/discuss.md +310 -0
  48. package/workflows/execute-plan.md +222 -0
  49. package/workflows/execute.md +338 -0
  50. package/workflows/new-app.md +331 -0
  51. package/workflows/new-milestone.md +258 -0
  52. package/workflows/next.md +157 -0
  53. package/workflows/plan.md +310 -0
  54. package/workflows/ship.md +296 -0
  55. package/workflows/status.md +145 -0
@@ -0,0 +1,395 @@
1
+ # Deployment Reference
2
+
3
+ Complete deployment guide for Frontier OS apps, covering Vercel configuration, CORS, app registration, environment variables, DNS, and webhooks.
4
+
5
+ ---
6
+
7
+ ## Vercel Deployment Configuration
8
+
9
+ All Frontier OS apps deploy to Vercel. The `vercel.json` file is identical across all apps.
10
+
11
+ ### `vercel.json` (Exact)
12
+
13
+ ```json
14
+ {
15
+ "rewrites": [
16
+ { "source": "/(.*)", "destination": "/index.html" }
17
+ ],
18
+ "headers": [
19
+ {
20
+ "source": "/(.*)",
21
+ "has": [
22
+ {
23
+ "type": "header",
24
+ "key": "Origin",
25
+ "value": "https://os.frontiertower.io"
26
+ }
27
+ ],
28
+ "headers": [
29
+ {
30
+ "key": "Access-Control-Allow-Origin",
31
+ "value": "https://os.frontiertower.io"
32
+ },
33
+ {
34
+ "key": "Access-Control-Allow-Methods",
35
+ "value": "GET, OPTIONS"
36
+ },
37
+ {
38
+ "key": "Access-Control-Allow-Headers",
39
+ "value": "Content-Type"
40
+ }
41
+ ]
42
+ },
43
+ {
44
+ "source": "/(.*)",
45
+ "has": [
46
+ {
47
+ "type": "header",
48
+ "key": "Origin",
49
+ "value": "https://alpha.os.frontiertower.io"
50
+ }
51
+ ],
52
+ "headers": [
53
+ {
54
+ "key": "Access-Control-Allow-Origin",
55
+ "value": "https://alpha.os.frontiertower.io"
56
+ },
57
+ {
58
+ "key": "Access-Control-Allow-Methods",
59
+ "value": "GET, OPTIONS"
60
+ },
61
+ {
62
+ "key": "Access-Control-Allow-Headers",
63
+ "value": "Content-Type"
64
+ }
65
+ ]
66
+ },
67
+ {
68
+ "source": "/(.*)",
69
+ "has": [
70
+ {
71
+ "type": "header",
72
+ "key": "Origin",
73
+ "value": "https://beta.os.frontiertower.io"
74
+ }
75
+ ],
76
+ "headers": [
77
+ {
78
+ "key": "Access-Control-Allow-Origin",
79
+ "value": "https://beta.os.frontiertower.io"
80
+ },
81
+ {
82
+ "key": "Access-Control-Allow-Methods",
83
+ "value": "GET, OPTIONS"
84
+ },
85
+ {
86
+ "key": "Access-Control-Allow-Headers",
87
+ "value": "Content-Type"
88
+ }
89
+ ]
90
+ },
91
+ {
92
+ "source": "/(.*)",
93
+ "has": [
94
+ {
95
+ "type": "header",
96
+ "key": "Origin",
97
+ "value": "https://sandbox.os.frontiertower.io"
98
+ }
99
+ ],
100
+ "headers": [
101
+ {
102
+ "key": "Access-Control-Allow-Origin",
103
+ "value": "https://sandbox.os.frontiertower.io"
104
+ },
105
+ {
106
+ "key": "Access-Control-Allow-Methods",
107
+ "value": "GET, OPTIONS"
108
+ },
109
+ {
110
+ "key": "Access-Control-Allow-Headers",
111
+ "value": "Content-Type"
112
+ }
113
+ ]
114
+ },
115
+ {
116
+ "source": "/(.*)",
117
+ "has": [
118
+ {
119
+ "type": "header",
120
+ "key": "Origin",
121
+ "value": "http://localhost:5173"
122
+ }
123
+ ],
124
+ "headers": [
125
+ {
126
+ "key": "Access-Control-Allow-Origin",
127
+ "value": "http://localhost:5173"
128
+ },
129
+ {
130
+ "key": "Access-Control-Allow-Methods",
131
+ "value": "GET, OPTIONS"
132
+ },
133
+ {
134
+ "key": "Access-Control-Allow-Headers",
135
+ "value": "Content-Type"
136
+ }
137
+ ]
138
+ }
139
+ ]
140
+ }
141
+ ```
142
+
143
+ ### Why 5 Separate Blocks
144
+
145
+ Vercel does not support multiple values in a single `Access-Control-Allow-Origin` header. Each allowed origin requires its own conditional header block using the `has` matcher. The `has` condition checks the incoming `Origin` request header and only attaches the CORS response headers when the origin matches.
146
+
147
+ ---
148
+
149
+ ## Allowed Origins
150
+
151
+ The Frontier Wallet PWA runs at these 5 origins. Apps must allow CORS from all of them:
152
+
153
+ | Origin | Environment | Description |
154
+ | ----------------------------------------- | ------------ | -------------------------------------------------------------- |
155
+ | `http://localhost:5173` | Development | Local Vite dev server for the PWA |
156
+ | `https://sandbox.os.frontiertower.io` | Sandbox | Sandbox environment |
157
+ | `https://alpha.os.frontiertower.io` | Alpha | Early access, design preview -- "there will be dragons" |
158
+ | `https://beta.os.frontiertower.io` | Beta | Internally QA'd and tested, no external audit |
159
+ | `https://os.frontiertower.io` | Production | Production ready |
160
+
161
+ These origins are also hardcoded in the SDK at `@frontiertower/frontier-sdk/ui-utils/detection.ts` as `ALLOWED_ORIGINS`.
162
+
163
+ The `isInFrontierApp()` function checks `window.self !== window.top` to detect if the app is running inside the Frontier Wallet iframe. The `getParentOrigin()` function resolves the parent frame's origin via `document.referrer` or `window.parent.location.origin`.
164
+
165
+ **Never use `Access-Control-Allow-Origin: *` in production.** Always use the exact origin list above.
166
+
167
+ ---
168
+
169
+ ## Security Headers
170
+
171
+ In addition to CORS, all deployment targets should include:
172
+
173
+ | Header | Recommended Value | Purpose |
174
+ | ---------------------------- | -------------------------------------------------- | ------------------------------------------- |
175
+ | `Content-Security-Policy` | Include `frame-ancestors` for all 5 Frontier origins | Prevents embedding by unauthorized sites |
176
+ | `X-Content-Type-Options` | `nosniff` | Prevents MIME-type sniffing |
177
+ | `Referrer-Policy` | `strict-origin-when-cross-origin` | Controls referrer information leakage |
178
+ | `Permissions-Policy` | Disable unnecessary browser APIs | Reduces attack surface |
179
+
180
+ The `frame-ancestors` CSP directive is critical because apps load inside the PWA's iframe. Without it, the browser may block the embed.
181
+
182
+ ---
183
+
184
+ ## App Registration
185
+
186
+ Apps must be registered with the Frontier platform before they appear in the AppStore.
187
+
188
+ ### Registration via ThirdParty SDK
189
+
190
+ The recommended way is to use the OS Developer app in the Frontier AppStore. The API alternative:
191
+
192
+ 1. **Get developer access** -- contact support@frontiertower.io to be added as a developer manager.
193
+ 2. **Get your developer profile** -- install the OS Developer app from the AppStore, or call `GET /third-party/developers/`.
194
+ 3. **Rotate your API key** immediately after receiving it: `POST /third-party/developers/{developer_id}/rotate-key/`. Store the new key securely.
195
+ 4. **Create the app** -- via OS Developer or `POST /third-party/apps/` with metadata: `name`, `url`, `description`, optional DNS entries.
196
+
197
+ ### App Status Lifecycle
198
+
199
+ ```
200
+ in_review --> accepted --> released
201
+ ^ |
202
+ |--- (any update) |
203
+ v
204
+ request_deactivation --> deactivated
205
+
206
+ in_review --> rejected
207
+ ```
208
+
209
+ - New apps start in `in_review`.
210
+ - Updating an app returns it to `in_review`.
211
+ - Deleting a released app sets `request_deactivation` (no hard-delete).
212
+
213
+ ### Permissions in Registry
214
+
215
+ When registering, declare the SDK permissions your app requires:
216
+
217
+ ```typescript
218
+ const APP_REGISTRY: AppMetadata[] = [
219
+ {
220
+ id: 'my-app',
221
+ url: 'https://my-app.appstore.frontiertower.io',
222
+ origin: 'https://my-app.appstore.frontiertower.io',
223
+ version: '1.0.0',
224
+ developer: {
225
+ name: 'Developer Name',
226
+ verified: true,
227
+ },
228
+ permissions: {
229
+ wallet: true,
230
+ storage: true,
231
+ notifications: false,
232
+ },
233
+ } as AppMetadata,
234
+ ];
235
+ ```
236
+
237
+ Permissions must match the SDK methods actually called in source code. The fos-verifier agent enforces this (see [verification-rules.md](verification-rules.md)).
238
+
239
+ ---
240
+
241
+ ## Environment Variables
242
+
243
+ Frontier OS apps use the standard Vite environment variable pattern.
244
+
245
+ ### `.env.local` (not committed)
246
+
247
+ ```bash
248
+ VITE_APP_NAME=My App
249
+ VITE_API_URL=https://api.example.com
250
+ ```
251
+
252
+ ### Convention
253
+
254
+ - All client-exposed variables must use the `VITE_` prefix.
255
+ - Access in code via `import.meta.env.VITE_APP_NAME`.
256
+ - `.env.local` is gitignored. Never commit secrets.
257
+ - For Vercel deployment, set environment variables in the Vercel dashboard under Project Settings > Environment Variables.
258
+
259
+ ---
260
+
261
+ ## DNS Configuration
262
+
263
+ Apps are hosted on the `appstore.frontiertower.io` subdomain.
264
+
265
+ ### Domain Pattern
266
+
267
+ ```
268
+ <app-name>.appstore.frontiertower.io
269
+ ```
270
+
271
+ Example: `kickstarter.appstore.frontiertower.io`
272
+
273
+ ### DNS Entries
274
+
275
+ Two DNS records are needed:
276
+
277
+ 1. **CNAME** -- points the subdomain to the Vercel deployment:
278
+ ```
279
+ <app-name>.appstore.frontiertower.io CNAME cname.vercel-dns.com
280
+ ```
281
+
282
+ 2. **TXT** -- Vercel domain verification:
283
+ ```
284
+ _vercel.<app-name>.appstore.frontiertower.io TXT vc-domain-verify=<token>
285
+ ```
286
+
287
+ Configure the custom domain in the Vercel dashboard after DNS propagation. Vercel automatically provisions an SSL certificate.
288
+
289
+ ---
290
+
291
+ ## Webhook Setup
292
+
293
+ Webhooks let the Frontier API push events to your backend the moment something changes.
294
+
295
+ ### Creating a Webhook
296
+
297
+ 1. Use the OS Developer app or call `POST /third-party/webhooks/`.
298
+ 2. Provide:
299
+ ```json
300
+ {
301
+ "name": "My app events",
302
+ "target_url": "https://my-backend.example.com/webhooks",
303
+ "config": {
304
+ "events": ["event:*"],
305
+ "scope": { "communities": [10], "users": "*" }
306
+ }
307
+ }
308
+ ```
309
+ 3. New webhooks start in `IN_REVIEW`. After review, they become `LIVE` and start receiving deliveries.
310
+
311
+ ### Webhook Lifecycle
312
+
313
+ - `IN_REVIEW` -- default after creation or config changes
314
+ - `LIVE` -- approved, receiving deliveries
315
+ - `REJECTED` -- not approved
316
+
317
+ ### Event Format
318
+
319
+ Events use `namespace:action` format. Subscriptions support wildcards (`event:*` or `*`).
320
+
321
+ Available namespaces: `addon_product`, `addon`, `community`, `guest_check_in`, `internship_pass`, `supply_request`, `event`, `location`, `room_booking`, `sponsor`, `sponsor_pass`, `smart_account`, `bridge_account`, `recovery_request`, `post`, `citizen_suggestion`, `vote`, `developer`, `app`, `webhook`, `user`, `profile`, `subscription`.
322
+
323
+ ### Delivery Payload
324
+
325
+ Each delivery is an HTTP POST:
326
+
327
+ ```json
328
+ {
329
+ "id": "{delivery_uuid}",
330
+ "event": "namespace:action",
331
+ "triggered_at": "2024-01-01T12:00:00Z",
332
+ "data": { }
333
+ }
334
+ ```
335
+
336
+ Headers on every delivery:
337
+ - `Content-Type: application/json`
338
+ - `X-Webhook-Event`
339
+ - `X-Webhook-Id`
340
+ - `X-Webhook-Timestamp`
341
+ - `X-Webhook-Signature`
342
+ - `X-Webhook-Signature-Algorithm: ed25519`
343
+
344
+ ### Signature Verification (Ed25519)
345
+
346
+ Verify every delivery before processing:
347
+
348
+ 1. Read `X-Webhook-Timestamp` from the request.
349
+ 2. Build the message: `<timestamp>.<canonical_json_body>` (JSON with sorted keys, no extra spaces).
350
+ 3. Verify `X-Webhook-Signature` (base64-encoded) using the webhook's Ed25519 public key.
351
+ 4. Reject if verification fails, timestamp is stale, or event is unexpected.
352
+
353
+ ```python
354
+ import base64, json
355
+ from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PublicKey
356
+
357
+ body = request.get_json(force=True)
358
+ timestamp = request.headers["X-Webhook-Timestamp"]
359
+ signature = base64.b64decode(request.headers["X-Webhook-Signature"])
360
+ message = f"{timestamp}.".encode() + json.dumps(body, separators=(",", ":"), sort_keys=True).encode()
361
+
362
+ public_key = Ed25519PublicKey.from_public_bytes(base64.b64decode(PUBLIC_KEY))
363
+ public_key.verify(signature, message) # Raises if invalid
364
+ ```
365
+
366
+ ### Rotating Webhook Signing Keys
367
+
368
+ Call `POST /third-party/webhooks/{webhook_id}/rotate-key/`. Deliveries immediately use the new key. Always trust the latest public key for each webhook.
369
+
370
+ ### Operational Tips
371
+
372
+ - Use HTTPS endpoints only.
373
+ - Treat `X-Webhook-Id` as an idempotency key.
374
+ - Keep one webhook per environment (staging vs production).
375
+ - Rotate signing keys and API keys regularly.
376
+
377
+ ---
378
+
379
+ ## Testing Deployment
380
+
381
+ After deploying, verify CORS is working:
382
+
383
+ ```bash
384
+ curl -I https://<app-name>.appstore.frontiertower.io \
385
+ -H "Origin: https://os.frontiertower.io"
386
+
387
+ # Should include:
388
+ # Access-Control-Allow-Origin: https://os.frontiertower.io
389
+ ```
390
+
391
+ Verify the app loads correctly:
392
+ 1. Check browser console for CORS errors.
393
+ 2. Confirm the app is accessible directly at its URL.
394
+ 3. Test inside the Frontier Wallet PWA across environments.
395
+ 4. Verify metadata (`<title>`, `<meta name="description">`, `<link rel="icon">`) is present in the HTML.