@editframe/create 0.44.0 → 0.45.1

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 (98) hide show
  1. package/dist/index.js +16 -28
  2. package/dist/index.js.map +1 -1
  3. package/dist/skills/editframe-brand-video-generator/README.md +155 -0
  4. package/dist/skills/editframe-brand-video-generator/SKILL.md +207 -0
  5. package/dist/skills/editframe-brand-video-generator/references/brand-examples.md +178 -0
  6. package/dist/skills/editframe-brand-video-generator/references/color-psychology.md +227 -0
  7. package/dist/skills/editframe-brand-video-generator/references/composition-patterns.md +383 -0
  8. package/dist/skills/editframe-brand-video-generator/references/editing.md +66 -0
  9. package/dist/skills/editframe-brand-video-generator/references/emotional-arcs.md +496 -0
  10. package/dist/skills/editframe-brand-video-generator/references/genre-selection.md +135 -0
  11. package/dist/skills/editframe-brand-video-generator/references/transition-styles.md +611 -0
  12. package/dist/skills/editframe-brand-video-generator/references/typography-personalities.md +326 -0
  13. package/dist/skills/editframe-brand-video-generator/references/video-archetypes.md +86 -0
  14. package/dist/skills/editframe-brand-video-generator/references/video-fundamentals.md +169 -0
  15. package/dist/skills/editframe-brand-video-generator/references/visual-metaphors.md +50 -0
  16. package/dist/skills/editframe-composition/SKILL.md +169 -0
  17. package/dist/skills/editframe-composition/references/audio.md +483 -0
  18. package/dist/skills/editframe-composition/references/captions.md +844 -0
  19. package/dist/skills/editframe-composition/references/composition-model.md +73 -0
  20. package/dist/skills/editframe-composition/references/configuration.md +403 -0
  21. package/dist/skills/editframe-composition/references/css-parts.md +105 -0
  22. package/dist/skills/editframe-composition/references/css-variables.md +640 -0
  23. package/dist/skills/editframe-composition/references/entry-points.md +810 -0
  24. package/dist/skills/editframe-composition/references/events.md +499 -0
  25. package/dist/skills/editframe-composition/references/getting-started.md +259 -0
  26. package/dist/skills/editframe-composition/references/hooks.md +234 -0
  27. package/dist/skills/editframe-composition/references/image.md +241 -0
  28. package/dist/skills/editframe-composition/references/r3f.md +580 -0
  29. package/dist/skills/editframe-composition/references/render-api.md +484 -0
  30. package/dist/skills/editframe-composition/references/render-strategies.md +119 -0
  31. package/dist/skills/editframe-composition/references/render-to-video.md +1101 -0
  32. package/dist/skills/editframe-composition/references/scripting.md +606 -0
  33. package/dist/skills/editframe-composition/references/sequencing.md +116 -0
  34. package/dist/skills/editframe-composition/references/server-rendering.md +753 -0
  35. package/dist/skills/editframe-composition/references/surface.md +329 -0
  36. package/dist/skills/editframe-composition/references/text.md +627 -0
  37. package/dist/skills/editframe-composition/references/time-model.md +99 -0
  38. package/dist/skills/editframe-composition/references/timegroup-modes.md +102 -0
  39. package/dist/skills/editframe-composition/references/timegroup.md +457 -0
  40. package/dist/skills/editframe-composition/references/timeline-root.md +398 -0
  41. package/dist/skills/editframe-composition/references/transcription.md +47 -0
  42. package/dist/skills/editframe-composition/references/transitions.md +608 -0
  43. package/dist/skills/editframe-composition/references/use-media-info.md +357 -0
  44. package/dist/skills/editframe-composition/references/video.md +506 -0
  45. package/dist/skills/editframe-composition/references/waveform.md +327 -0
  46. package/dist/skills/editframe-editor-gui/SKILL.md +152 -0
  47. package/dist/skills/editframe-editor-gui/references/active-root-temporal.md +657 -0
  48. package/dist/skills/editframe-editor-gui/references/canvas.md +947 -0
  49. package/dist/skills/editframe-editor-gui/references/controls.md +366 -0
  50. package/dist/skills/editframe-editor-gui/references/dial.md +756 -0
  51. package/dist/skills/editframe-editor-gui/references/editor-toolkit.md +587 -0
  52. package/dist/skills/editframe-editor-gui/references/filmstrip.md +460 -0
  53. package/dist/skills/editframe-editor-gui/references/fit-scale.md +772 -0
  54. package/dist/skills/editframe-editor-gui/references/focus-overlay.md +561 -0
  55. package/dist/skills/editframe-editor-gui/references/hierarchy.md +544 -0
  56. package/dist/skills/editframe-editor-gui/references/overlay-item.md +634 -0
  57. package/dist/skills/editframe-editor-gui/references/overlay-layer.md +429 -0
  58. package/dist/skills/editframe-editor-gui/references/pan-zoom.md +568 -0
  59. package/dist/skills/editframe-editor-gui/references/pause.md +397 -0
  60. package/dist/skills/editframe-editor-gui/references/play.md +370 -0
  61. package/dist/skills/editframe-editor-gui/references/preview.md +391 -0
  62. package/dist/skills/editframe-editor-gui/references/resizable-box.md +749 -0
  63. package/dist/skills/editframe-editor-gui/references/scrubber.md +588 -0
  64. package/dist/skills/editframe-editor-gui/references/thumbnail-strip.md +566 -0
  65. package/dist/skills/editframe-editor-gui/references/time-display.md +492 -0
  66. package/dist/skills/editframe-editor-gui/references/timeline-ruler.md +489 -0
  67. package/dist/skills/editframe-editor-gui/references/timeline.md +604 -0
  68. package/dist/skills/editframe-editor-gui/references/toggle-loop.md +618 -0
  69. package/dist/skills/editframe-editor-gui/references/toggle-play.md +526 -0
  70. package/dist/skills/editframe-editor-gui/references/transform-handles.md +924 -0
  71. package/dist/skills/editframe-editor-gui/references/trim-handles.md +725 -0
  72. package/dist/skills/editframe-editor-gui/references/workbench.md +453 -0
  73. package/dist/skills/editframe-motion-design/SKILL.md +101 -0
  74. package/dist/skills/editframe-motion-design/references/0-editframe.md +299 -0
  75. package/dist/skills/editframe-motion-design/references/1-intent.md +201 -0
  76. package/dist/skills/editframe-motion-design/references/2-physics-model.md +405 -0
  77. package/dist/skills/editframe-motion-design/references/3-attention.md +350 -0
  78. package/dist/skills/editframe-motion-design/references/4-process.md +418 -0
  79. package/dist/skills/editframe-vite-plugin/SKILL.md +75 -0
  80. package/dist/skills/editframe-vite-plugin/references/file-api.md +111 -0
  81. package/dist/skills/editframe-vite-plugin/references/getting-started.md +96 -0
  82. package/dist/skills/editframe-vite-plugin/references/jit-transcoding.md +91 -0
  83. package/dist/skills/editframe-vite-plugin/references/local-assets.md +75 -0
  84. package/dist/skills/editframe-vite-plugin/references/visual-testing.md +136 -0
  85. package/dist/skills/editframe-webhooks/SKILL.md +126 -0
  86. package/dist/skills/editframe-webhooks/references/events.md +382 -0
  87. package/dist/skills/editframe-webhooks/references/getting-started.md +232 -0
  88. package/dist/skills/editframe-webhooks/references/security.md +418 -0
  89. package/dist/skills/editframe-webhooks/references/testing.md +409 -0
  90. package/dist/skills/editframe-webhooks/references/troubleshooting.md +457 -0
  91. package/dist/templates/html/AGENTS.md +13 -0
  92. package/dist/templates/react/AGENTS.md +13 -0
  93. package/dist/utils.js +15 -16
  94. package/dist/utils.js.map +1 -1
  95. package/package.json +1 -1
  96. package/tsdown.config.ts +4 -0
  97. package/dist/detectAgent.js +0 -89
  98. package/dist/detectAgent.js.map +0 -1
@@ -0,0 +1,418 @@
1
+ ---
2
+ title: Webhook Security
3
+ description: Verify HMAC-SHA256 signatures on incoming webhook requests and prevent replay attacks with timestamp validation.
4
+ type: reference
5
+ nav:
6
+ parent: "Security"
7
+ priority: 3
8
+ ---
9
+
10
+ # Webhook Security
11
+
12
+ Secure your webhook endpoints with HMAC signature verification, timestamp validation, and proper secret management.
13
+
14
+ ## HMAC Signature Verification
15
+
16
+ Every webhook request includes an `X-Webhook-Signature` header containing an HMAC-SHA256 signature. This signature proves the request originated from Editframe and hasn't been tampered with.
17
+
18
+ ### Signature Format
19
+
20
+ ```
21
+ X-Webhook-Signature: <hmac-sha256-hex-digest>
22
+ ```
23
+
24
+ The signature is computed as:
25
+ ```
26
+ HMAC-SHA256(webhook_secret, request_body)
27
+ ```
28
+
29
+ Where:
30
+ - `webhook_secret` is your API key's webhook secret
31
+ - `request_body` is the raw JSON request body as a string
32
+
33
+ ### Verification Implementation
34
+
35
+ #### Node.js / TypeScript
36
+
37
+ ```typescript
38
+ import crypto from "node:crypto";
39
+
40
+ function verifyWebhookSignature(
41
+ payload: string | Buffer,
42
+ signature: string,
43
+ secret: string
44
+ ): boolean {
45
+ // Compute expected signature
46
+ const expectedSignature = crypto
47
+ .createHmac("sha256", secret)
48
+ .update(payload)
49
+ .digest("hex");
50
+
51
+ // Use timing-safe comparison to prevent timing attacks
52
+ return crypto.timingSafeEqual(
53
+ Buffer.from(signature),
54
+ Buffer.from(expectedSignature)
55
+ );
56
+ }
57
+
58
+ // Express middleware
59
+ app.post("/webhooks/editframe", (req, res) => {
60
+ const signature = req.headers["x-webhook-signature"] as string;
61
+ const secret = process.env.EDITFRAME_WEBHOOK_SECRET!;
62
+
63
+ // Get raw body (before JSON parsing)
64
+ const rawBody = JSON.stringify(req.body);
65
+
66
+ if (!verifyWebhookSignature(rawBody, signature, secret)) {
67
+ return res.status(401).send("Invalid signature");
68
+ }
69
+
70
+ // Signature is valid - process event
71
+ const event = req.body;
72
+ res.status(200).send("OK");
73
+ });
74
+ ```
75
+
76
+ **Critical**: Hash the raw request body string, not the parsed JSON object. JSON serialization order can vary, producing different hashes.
77
+
78
+ #### Express with Raw Body
79
+
80
+ To verify signatures correctly, you need access to the raw request body:
81
+
82
+ ```typescript
83
+ import express from "express";
84
+ import bodyParser from "body-parser";
85
+
86
+ const app = express();
87
+
88
+ // Capture raw body for signature verification
89
+ app.use(bodyParser.json({
90
+ verify: (req, res, buf) => {
91
+ (req as any).rawBody = buf.toString('utf8');
92
+ }
93
+ }));
94
+
95
+ app.post("/webhooks/editframe", (req, res) => {
96
+ const signature = req.headers["x-webhook-signature"] as string;
97
+ const rawBody = (req as any).rawBody;
98
+ const secret = process.env.EDITFRAME_WEBHOOK_SECRET!;
99
+
100
+ if (!verifyWebhookSignature(rawBody, signature, secret)) {
101
+ return res.status(401).send("Invalid signature");
102
+ }
103
+
104
+ // Process event
105
+ res.status(200).send("OK");
106
+ });
107
+ ```
108
+
109
+ #### Next.js API Routes
110
+
111
+ ```typescript
112
+ import { NextApiRequest, NextApiResponse } from "next";
113
+ import crypto from "node:crypto";
114
+
115
+ // Disable body parsing to access raw body
116
+ export const config = {
117
+ api: {
118
+ bodyParser: false,
119
+ },
120
+ };
121
+
122
+ async function getRawBody(req: NextApiRequest): Promise<string> {
123
+ return new Promise((resolve, reject) => {
124
+ let data = "";
125
+ req.on("data", (chunk) => { data += chunk; });
126
+ req.on("end", () => resolve(data));
127
+ req.on("error", reject);
128
+ });
129
+ }
130
+
131
+ export default async function handler(
132
+ req: NextApiRequest,
133
+ res: NextApiResponse
134
+ ) {
135
+ if (req.method !== "POST") {
136
+ return res.status(405).send("Method not allowed");
137
+ }
138
+
139
+ const signature = req.headers["x-webhook-signature"] as string;
140
+ const rawBody = await getRawBody(req);
141
+ const secret = process.env.EDITFRAME_WEBHOOK_SECRET!;
142
+
143
+ const expectedSignature = crypto
144
+ .createHmac("sha256", secret)
145
+ .update(rawBody)
146
+ .digest("hex");
147
+
148
+ if (signature !== expectedSignature) {
149
+ return res.status(401).send("Invalid signature");
150
+ }
151
+
152
+ const event = JSON.parse(rawBody);
153
+ // Process event
154
+
155
+ res.status(200).send("OK");
156
+ }
157
+ ```
158
+
159
+ #### Python (Flask)
160
+
161
+ ```python
162
+ import hmac
163
+ import hashlib
164
+ from flask import Flask, request
165
+
166
+ app = Flask(__name__)
167
+ WEBHOOK_SECRET = "your-webhook-secret"
168
+
169
+ @app.route("/webhooks/editframe", methods=["POST"])
170
+ def handle_webhook():
171
+ signature = request.headers.get("X-Webhook-Signature")
172
+ payload = request.get_data()
173
+
174
+ expected_signature = hmac.new(
175
+ WEBHOOK_SECRET.encode(),
176
+ payload,
177
+ hashlib.sha256
178
+ ).hexdigest()
179
+
180
+ if not hmac.compare_digest(signature, expected_signature):
181
+ return "Invalid signature", 401
182
+
183
+ event = request.get_json()
184
+ # Process event
185
+
186
+ return "OK", 200
187
+ ```
188
+
189
+ #### Ruby (Rails)
190
+
191
+ ```ruby
192
+ require 'openssl'
193
+
194
+ class WebhooksController < ApplicationController
195
+ skip_before_action :verify_authenticity_token
196
+
197
+ def editframe
198
+ signature = request.headers["X-Webhook-Signature"]
199
+ payload = request.raw_post
200
+ secret = ENV["EDITFRAME_WEBHOOK_SECRET"]
201
+
202
+ expected_signature = OpenSSL::HMAC.hexdigest(
203
+ OpenSSL::Digest.new('sha256'),
204
+ secret,
205
+ payload
206
+ )
207
+
208
+ unless ActiveSupport::SecurityUtils.secure_compare(signature, expected_signature)
209
+ render plain: "Invalid signature", status: :unauthorized
210
+ return
211
+ end
212
+
213
+ event = JSON.parse(payload)
214
+ # Process event
215
+
216
+ render plain: "OK", status: :ok
217
+ end
218
+ end
219
+ ```
220
+
221
+ ## Secret Management
222
+
223
+ ### Retrieving Your Webhook Secret
224
+
225
+ When you create an API key, you receive:
226
+ 1. **API Key**: For authenticating API requests
227
+ 2. **Webhook Secret**: For verifying webhook signatures
228
+
229
+ **Important**: The webhook secret is only shown once during API key creation. Store it securely immediately.
230
+
231
+ ### Storing Secrets
232
+
233
+ Never hardcode secrets in your application code. Use environment variables or a secrets manager:
234
+
235
+ ```bash
236
+ # .env
237
+ EDITFRAME_API_KEY=ef_live_...
238
+ EDITFRAME_WEBHOOK_SECRET=abc123...
239
+ ```
240
+
241
+ ```typescript
242
+ // Load from environment
243
+ const secret = process.env.EDITFRAME_WEBHOOK_SECRET;
244
+ if (!secret) {
245
+ throw new Error("EDITFRAME_WEBHOOK_SECRET not configured");
246
+ }
247
+ ```
248
+
249
+ ### Rotating Secrets
250
+
251
+ To rotate your webhook secret:
252
+
253
+ 1. Go to your API key detail page
254
+ 2. Click "Regenerate Webhook Secret"
255
+ 3. Update your application with the new secret
256
+ 4. Deploy the updated application
257
+
258
+ **Warning**: Old signatures will fail after secret rotation. Update your application before rotating secrets in production.
259
+
260
+ ## Replay Attack Prevention
261
+
262
+ Prevent replay attacks by implementing timestamp validation:
263
+
264
+ ```typescript
265
+ interface WebhookEvent {
266
+ topic: string;
267
+ data: {
268
+ created_at: string; // ISO 8601 timestamp
269
+ // ... other fields
270
+ };
271
+ }
272
+
273
+ function isWebhookRecent(event: WebhookEvent, maxAgeSeconds: number = 300): boolean {
274
+ const eventTime = new Date(event.data.created_at).getTime();
275
+ const now = Date.now();
276
+ const ageSeconds = (now - eventTime) / 1000;
277
+
278
+ return ageSeconds >= 0 && ageSeconds <= maxAgeSeconds;
279
+ }
280
+
281
+ // In webhook handler
282
+ app.post("/webhooks/editframe", (req, res) => {
283
+ const event = req.body;
284
+
285
+ // Verify signature first
286
+ if (!verifySignature(req)) {
287
+ return res.status(401).send("Invalid signature");
288
+ }
289
+
290
+ // Check timestamp (5 minute tolerance)
291
+ if (!isWebhookRecent(event, 300)) {
292
+ return res.status(400).send("Webhook timestamp out of range");
293
+ }
294
+
295
+ // Process event
296
+ res.status(200).send("OK");
297
+ });
298
+ ```
299
+
300
+ ## Idempotency
301
+
302
+ Webhooks may be delivered multiple times due to retries. Implement idempotency to prevent duplicate processing:
303
+
304
+ ```typescript
305
+ const processedEventIds = new Set<string>();
306
+
307
+ app.post("/webhooks/editframe", async (req, res) => {
308
+ const event = req.body;
309
+ const eventId = `${event.topic}:${event.data.id}`;
310
+
311
+ // Check if already processed
312
+ if (processedEventIds.has(eventId)) {
313
+ console.log(`Duplicate webhook ignored: ${eventId}`);
314
+ return res.status(200).send("OK"); // Still return 200 to stop retries
315
+ }
316
+
317
+ // Verify signature
318
+ if (!verifySignature(req)) {
319
+ return res.status(401).send("Invalid signature");
320
+ }
321
+
322
+ // Process event
323
+ await processWebhookEvent(event);
324
+
325
+ // Mark as processed
326
+ processedEventIds.add(eventId);
327
+
328
+ res.status(200).send("OK");
329
+ });
330
+ ```
331
+
332
+ For production, use a database or Redis to track processed events:
333
+
334
+ ```typescript
335
+ import { db } from "./database";
336
+
337
+ async function isEventProcessed(eventId: string): Promise<boolean> {
338
+ const record = await db.query(
339
+ "SELECT 1 FROM processed_webhooks WHERE event_id = $1",
340
+ [eventId]
341
+ );
342
+ return record.rows.length > 0;
343
+ }
344
+
345
+ async function markEventProcessed(eventId: string): Promise<void> {
346
+ await db.query(
347
+ "INSERT INTO processed_webhooks (event_id, processed_at) VALUES ($1, NOW())",
348
+ [eventId]
349
+ );
350
+ }
351
+ ```
352
+
353
+ ## Security Checklist
354
+
355
+ - [ ] Verify HMAC signature on every webhook request
356
+ - [ ] Use timing-safe comparison for signature verification
357
+ - [ ] Hash the raw request body, not parsed JSON
358
+ - [ ] Store webhook secrets in environment variables or secrets manager
359
+ - [ ] Implement timestamp validation to prevent replay attacks
360
+ - [ ] Implement idempotency to prevent duplicate processing
361
+ - [ ] Use HTTPS for webhook endpoints (required)
362
+ - [ ] Return 200 OK quickly, process events asynchronously
363
+ - [ ] Log signature verification failures for security monitoring
364
+ - [ ] Rotate webhook secrets periodically
365
+
366
+ ## Common Security Mistakes
367
+
368
+ ### 1. Hashing Parsed JSON
369
+
370
+ **Wrong:**
371
+ ```typescript
372
+ const signature = crypto
373
+ .createHmac("sha256", secret)
374
+ .update(JSON.stringify(req.body)) // Different serialization!
375
+ .digest("hex");
376
+ ```
377
+
378
+ **Right:**
379
+ ```typescript
380
+ const signature = crypto
381
+ .createHmac("sha256", secret)
382
+ .update(req.rawBody) // Raw body as received
383
+ .digest("hex");
384
+ ```
385
+
386
+ ### 2. Non-Timing-Safe Comparison
387
+
388
+ **Wrong:**
389
+ ```typescript
390
+ if (signature === expectedSignature) { // Vulnerable to timing attacks
391
+ // ...
392
+ }
393
+ ```
394
+
395
+ **Right:**
396
+ ```typescript
397
+ if (crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expectedSignature))) {
398
+ // ...
399
+ }
400
+ ```
401
+
402
+ ### 3. Ignoring Signature Verification
403
+
404
+ **Never do this:**
405
+ ```typescript
406
+ // DON'T: Process webhooks without verification
407
+ app.post("/webhooks/editframe", (req, res) => {
408
+ // No signature check - anyone can send fake webhooks!
409
+ processEvent(req.body);
410
+ res.status(200).send("OK");
411
+ });
412
+ ```
413
+
414
+ ## Next Steps
415
+
416
+ - [testing.md](references/testing.md) — Test signature verification locally
417
+ - [troubleshooting.md](references/troubleshooting.md) — Debug signature verification issues
418
+ - [getting-started.md](references/getting-started.md) — Complete webhook setup guide