@ericrisco/rsc 0.1.48 → 0.1.49

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/manifest.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "0.1.48",
2
+ "version": "0.1.49",
3
3
  "counts": {
4
4
  "skills": 258
5
5
  },
@@ -1332,7 +1332,7 @@
1332
1332
  },
1333
1333
  {
1334
1334
  "id": "domains-dns",
1335
- "description": "Use when pointing a custom domain at a host, fixing broken HTTPS, choosing apex vs www, or publishing DNS rows — registering/delegating a name, writing A/AAAA/CNAME/ALIAS/MX/TXT/CAA/SRV records, standing up auto-renewing TLS, or cutting nameservers over without downtime. Triggers: 'point example.com at Vercel', 'set up DNS for the domain', 'NET::ERR_CERT_AUTHORITY_INVALID', 'CNAME flattening / ALIAS at the apex', 'dig +trace still shows the old IP', 'need a CAA record for Let's Encrypt', 'publish the SPF/DKIM/DMARC/MX rows', 'apuntar el dominio', 'el certificado caducó y no renueva', 'configurar els registres DNS'. NOT inbox placement / warmup / spam-rate tuning (that is email-deliverability), NOT what runs behind the name (that is deployment).",
1335
+ "description": "Use when pointing a domain at a host or fixing broken HTTPS — delegating nameservers, writing A/AAAA/CNAME/ALIAS/MX/TXT/CAA rows, apex vs www, standing up auto-renewing TLS, and cutting nameservers over without downtime. NOT inbox placement or warmup (that is `email-deliverability`), NOT what runs behind the name (that is `deployment`).",
1336
1336
  "tags": [
1337
1337
  "dns",
1338
1338
  "tls",
@@ -1356,7 +1356,7 @@
1356
1356
  },
1357
1357
  {
1358
1358
  "id": "drizzle-orm",
1359
- "description": "Use when modeling data or querying with Drizzle ORM in TypeScript — schema declared in `.ts`, type-safe `select`/`insert`/relational queries, and `drizzle-kit` migrations, or when choosing Drizzle over Prisma for an edge/serverless runtime. Triggers: 'set up Drizzle ORM', 'drizzle-kit generate', 'write a pgTable schema', '$inferSelect type', 'drizzle push vs generate for production', 'my db.query.posts.findMany returns no author relation', 'drizzle-kit conflicting migration', 'switch off Prisma to something lighter for edge', 'definir l esquema amb Drizzle i tipus type-safe', 'migrar de Prisma a Drizzle para algo más ligero', 'migració amb drizzle-kit'. NOT Prisma Client or schema.prisma (that is prisma-orm), NOT a generic zero-downtime migration strategy (that is db-migrations), NOT Postgres engine/indexing/EXPLAIN (that is postgresdb).",
1359
+ "description": "Use when modeling data or querying with Drizzle ORM in TypeScript — `pgTable` schema in `.ts`, type-safe select/insert/relational queries, `drizzle-kit` migrations. NOT Prisma Client or `schema.prisma` (that is `prisma-orm`), NOT ORM-agnostic migration strategy (that is `db-migrations`), NOT Postgres engine tuning or EXPLAIN (that is `postgresdb`).",
1360
1360
  "tags": [
1361
1361
  "drizzle",
1362
1362
  "drizzle-orm",
@@ -1382,7 +1382,7 @@
1382
1382
  },
1383
1383
  {
1384
1384
  "id": "duckdb",
1385
- "description": "Use when you need fast analytical SQL over local files (Parquet/CSV/JSON/Arrow) with no server to run, when embedding OLAP in an app or notebook, when a pandas groupby/merge on multi-GB data is too slow, or when reading data straight from S3/HTTP/a lakehouse. Triggers: 'query a folder of parquet files in place', 'aggregate a 5GB CSV on my laptop', 'replace this slow pandas groupby', 'run SQL over a dataframe in Jupyter', 'read these parquet files from S3 without downloading', 'out-of-core aggregate', 'analizar un CSV enorme sin montar un servidor', 'consultar parquet en local sense base de dades'. NOT a multi-user production analytics server (that is clickhouse-analytics), NOT your app's transactional CRUD database (that is postgresdb).",
1385
+ "description": "Use when analytical SQL must run in-process with no server: Parquet/CSV/JSON/Arrow queried in place, OLAP embedded in an app or notebook, a slow pandas groupby on multi-GB data, or S3/lakehouse data read without downloading. NOT a multi-user analytics server (that is clickhouse-analytics), NOT an app's transactional CRUD store (that is postgresdb).",
1386
1386
  "tags": [
1387
1387
  "duckdb",
1388
1388
  "olap",
@@ -1404,7 +1404,7 @@
1404
1404
  },
1405
1405
  {
1406
1406
  "id": "dynamodb",
1407
- "description": "Use when designing or operating an Amazon DynamoDB table - modeling entities and relationships without joins, choosing partition/sort keys, deciding single-table vs table-per-entity, adding a GSI/LSI, picking on-demand vs provisioned capacity, or diagnosing throttling. Triggers: 'design a DynamoDB table', 'model one-to-many / many-to-many without joins', 'single-table design worth it?', 'query the same data by two different keys' (inverted GSI), 'writes throttled even though I'm under my provisioned capacity' (hot partition), 'on-demand or provisioned?', 'why does my Query only return some rows?' (1 MB page), 'diseña la tabla DynamoDB para estos patrones de acceso', 'modela aquesta relació amb clau composta a DynamoDB'. NOT relational schema / SQL / EXPLAIN / B-tree indexing (that is postgresdb), and NOT document modeling with aggregation pipelines (that is mongodb).",
1407
+ "description": "Use when modeling or operating a DynamoDB table: deriving partition/sort keys from access patterns, single-table vs table-per-entity, adding a GSI/LSI, on-demand vs provisioned capacity, or diagnosing hot-partition throttling. NOT relational schema/SQL/EXPLAIN (that is `postgresdb`), NOT aggregation-pipeline document modeling (that is `mongodb`).",
1408
1408
  "tags": [
1409
1409
  "dynamodb",
1410
1410
  "nosql",
@@ -1425,7 +1425,7 @@
1425
1425
  },
1426
1426
  {
1427
1427
  "id": "e-signature",
1428
- "description": "Use when adding legally-valid electronic-signature flows to documents — sending a PDF or template for signature, embedding signing in your own app, tracking status, verifying signing webhooks, and retrieving the signed PDF plus its audit trail via DocuSign or Dropbox Sign. Triggers: 'send this contract for signature', 'create a DocuSign envelope', 'signature_request/send', 'embed signing in our app', 'notify us when it's signed', 'set up DocuSign JWT auth', 'is a typed-name signature legally binding?', 'do we need QES for EU customers?', 'enviar a firmar', 'firma electrònica amb validesa legal'. NOT drafting the contract text itself (that is contracts) and NOT OCR/parsing/extracting fields from documents (that is document-processing).",
1428
+ "description": "Use when wiring an e-signature flow with DocuSign or Dropbox Sign — picking the SES/AES/QES legal tier, sending a PDF or template for signature, embedded signing, verifying signing webhooks, retrieving the signed PDF plus audit trail. NOT drafting contract text (that is `contracts`), NOT extracting fields from PDFs (that is `document-processing`).",
1429
1429
  "tags": [
1430
1430
  "e-signature",
1431
1431
  "docusign",
@@ -1467,7 +1467,7 @@
1467
1467
  },
1468
1468
  {
1469
1469
  "id": "electron",
1470
- "description": "Use when building, hardening, or shipping a cross-platform desktop app with Electron — wiring the main/renderer/preload process model, exposing OS capabilities to a web UI over typed IPC, locking down an insecure shell, or packaging with code signing and auto-update. Triggers: 'build a desktop app for Mac/Windows/Linux from my web app', 'let my renderer read a file safely', 'audit my Electron security (nodeIntegration, sandbox, CSP)', 'my preload exposes ipcRenderer and the renderer can require(\\\"fs\\\")', 'Squirrel.Mac won\\\\'t auto-update my app', 'migrate off @electron/remote and BrowserView', 'empaquetar mi app de escritorio para Windows y Mac con firma de código y auto-actualización', 'empaquetar app d\\\\'escriptori amb auto-actualització'. NOT a smaller Rust-backed shell on the OS native webview (that is tauri).",
1470
+ "description": "Use when building, hardening, or shipping a cross-platform Electron desktop app — main/renderer/preload process model, typed contextBridge IPC, locking down nodeIntegration/contextIsolation/sandbox/CSP, or packaging with signing and auto-update. NOT a Rust-backed shell on the native webview (that is tauri), nor the web UI inside it (that is react).",
1471
1471
  "tags": [
1472
1472
  "electron",
1473
1473
  "desktop",
@@ -1488,7 +1488,7 @@
1488
1488
  },
1489
1489
  {
1490
1490
  "id": "elixir",
1491
- "description": "Use when writing or refactoring Elixir/OTP code — GenServers, supervision trees, processes, pattern matching, mix projects and releases — or when BEAM behaviour misbehaves (restart loops, mailbox growth, timeouts). Triggers: 'write a GenServer', 'design a supervision tree', 'restart strategy one_for_one', 'let it crash instead of try/rescue', 'mix release for production', non-obvious 'my process mailbox keeps growing', 'GenServer call timeout / restart loop', Catalan 'arbre de supervisio en Elixir / processos que peten', Spanish 'arbol de supervision / procesos en Elixir / que es let it crash'. NOT building a Phoenix web app, LiveView, Ecto schemas, controllers or channels (that is phoenix).",
1491
+ "description": "Use when writing or refactoring Elixir/OTP on the BEAM — GenServers, supervision trees and restart strategies, pattern matching, mix projects and releases — or when processes misbehave (restart loops, mailbox growth, call timeouts). NOT a Phoenix web app, LiveView, Ecto or channels (that is `phoenix`).",
1492
1492
  "tags": [
1493
1493
  "elixir",
1494
1494
  "otp",
@@ -1530,7 +1530,7 @@
1530
1530
  },
1531
1531
  {
1532
1532
  "id": "email-deliverability",
1533
- "description": "Use when mail you send lands in spam, gets rejected, or fails the Gmail/Yahoo bulk-sender checks, and you must fix authentication and reputation rather than the sending code — setting SPF/DKIM/DMARC DNS records so they align, raising a cold domain through warmup, keeping the spam-complaint rate under threshold, adding one-click unsubscribe, getting the brand logo and verified checkmark to show via BIMI, and reading why 'soft' rejections and Postmaster Tools say you are throttled. Triggers: 'our emails go to spam since the Gmail update', 'DMARC fails even though SPF passes', 'how do I warm up a new sending domain', 'do I need one-click unsubscribe to send to gmail', '550 5.7.26 unauthenticated', 'how do I get our logo and the blue checkmark to show in Gmail with BIMI', 'los correos caen en spam y el SPF está bien', 'configurar BIMI i el logo verificat al correu'. NOT putting transactional/bulk email on the wire through a provider API (that is email-connector).",
1533
+ "description": "Use when mail already sent lands in spam, is rejected, or fails the Gmail/Yahoo bulk-sender checks and the fix is authentication and reputation, not the sending code: SPF/DKIM/DMARC alignment, domain warmup, spam-complaint rate, one-click unsubscribe, BIMI. NOT putting mail on the wire through a provider API (that is `email-connector`).",
1534
1534
  "tags": [
1535
1535
  "email",
1536
1536
  "deliverability",
@@ -1552,7 +1552,7 @@
1552
1552
  },
1553
1553
  {
1554
1554
  "id": "embeddings-search",
1555
- "description": "Use when results from a semantic search feel irrelevant, when you don't know which embedding model or chunk size to use, when you need to add a reranker or hybrid scoring, or when you have no way to tell whether a retrieval change actually helped. Symptoms: search returns garbage, exact-match queries miss, paraphrases work but keywords don't, recall looks fine but the top result is wrong, every chunking idea is a guess. Triggers: 'which embedding model and chunk size should I use', 'my semantic search returns irrelevant results', 'how do I measure if retrieval is good — recall@k, nDCG', 'add a reranker and hybrid BM25+vector', 'exact product codes return nothing but paraphrases work fine', 'recall@k is fine but the top answer is wrong', 'quin model d'embeddings i quina mida de chunk faig servir per a aquests documents', 'la cerca semàntica retorna resultats irrellevants'. NOT operating the vector store (that is vector-db).",
1555
+ "description": "Use when choosing an embedding model, chunk size, or query form, when semantic search returns irrelevant results, when adding hybrid BM25+vector or a reranker, or when a retrieval change needs a number (recall@k, nDCG, MRR). NOT operating the store — index tuning, quantization (that is `vector-db`) — nor the retrieve-to-answer loop (that is `rag`).",
1556
1556
  "tags": [
1557
1557
  "embeddings",
1558
1558
  "semantic-search",
@@ -1598,7 +1598,7 @@
1598
1598
  },
1599
1599
  {
1600
1600
  "id": "expo",
1601
- "description": "Use when building, shipping, or hot-patching a React Native app with Expo — EAS Build/Submit/Update, eas.json build profiles, config plugins, prebuild/CNG, the managed workflow, confirming you are on the New Architecture (mandatory since SDK 55). Triggers: 'eas build', 'eas.json', 'config plugin', 'expo prebuild', 'my OTA update isn't showing up on devices', 'runtime version mismatch', 'how do I add a permission without editing Xcode/Gradle', 'newArchEnabled is gone from app.json', 'com publico una actualització OTA sense passar per la store', 'subir la app a la Play Store con EAS'. NOT generic React Native components/navigation/native-module authoring (that is react-native), NOT a Dart mobile app (that is flutter).",
1601
+ "description": "Use when shipping a React Native app with Expo — EAS Build/Submit/Update, eas.json profiles and channels, config plugins, prebuild/CNG, runtime-version policy, OTA updates that never land, SDK upgrades, the New Architecture. NOT RN UI, navigation or native-module authoring (that is `react-native`), NOT a Dart app (that is `flutter`).",
1602
1602
  "tags": [
1603
1603
  "expo",
1604
1604
  "eas",
@@ -1641,7 +1641,7 @@
1641
1641
  },
1642
1642
  {
1643
1643
  "id": "fal",
1644
- "description": "Use when calling a fal.ai model endpoint by id to generate an image, audio, or video from JS/Python/curl, wiring the queue (subscribe vs submit), receiving results by webhook and verifying the ED25519 signature, estimating per-call cost, or migrating off the deprecated @fal-ai/serverless-client. Use when a fal request is stuck IN_QUEUE, a webhook never arrives or its signature won't verify, or a long video gen drops mid-call. Triggers: 'generate an image with fal-ai/flux/dev', 'subscribe vs submit on fal', 'my fal webhook signature won't verify', 'fal request stuck IN_QUEUE', 'how much will 500 Seedream images cost on fal', 'migrate off @fal-ai/serverless-client', 'genera esta imagen con fal y guarda la URL', 'munta el webhook de fal i verifica la firma'. NOT picking the model or art direction (that is ai-media).",
1644
+ "description": "Use when calling a fal.ai endpoint by id to generate image, audio, or video from JS/Python/curl: subscribe vs submit, queue states, ED25519 webhook signature verification, per-call cost, or migrating off @fal-ai/serverless-client. NOT which model or art direction (that is ai-media); NOT the same models on another platform (that is replicate).",
1645
1645
  "tags": [
1646
1646
  "fal",
1647
1647
  "fal-ai",
@@ -1680,7 +1680,7 @@
1680
1680
  },
1681
1681
  {
1682
1682
  "id": "finance-ops",
1683
- "description": "Use when running the money side of a small company on a cash basis — building a rolling cash-flow forecast, reconciling the bank against the books, fixing a messy expense-category map, or driving a month-end close. Triggers: 'are we going to run out of cash', 'what's our runway', '13-week cash flow forecast', 'reconcile the bank statement', 'these two numbers don't match', 'the ending balance doesn't roll into next week', 'everything landed in Other expenses', 'close the books for May', 'what's our burn this month', 'tancar el mes', 'conciliar el banc amb els llibres', 'cuánto runway nos queda al ritmo actual'. NOT recording journal entries or payroll/depreciation postings (that is bookkeeping).",
1683
+ "description": "Use when running the money side of a small company on a cash basis: a 13-week rolling cash-flow forecast and runway, bank-vs-books reconciliation, mapping spend to tax-return expense categories, or a month-end close. NOT posting journal entries, payroll or depreciation (that is `bookkeeping`); NOT multi-year projections (that is `financial-model`).",
1684
1684
  "tags": [
1685
1685
  "cash-flow",
1686
1686
  "reconciliation",
@@ -1702,7 +1702,7 @@
1702
1702
  },
1703
1703
  {
1704
1704
  "id": "financial-model",
1705
- "description": "Use when a founder needs the multi-period, driver-based projection that raises a round and steers the company — build 3-year projections, model revenue + costs + headcount + runway, size the raise, or stress-test scenarios. Triggers: 'build a financial model for our seed', 'model our revenue costs and runway', 'when do we run out of cash', 'how much should we raise to get 24 months', 'what runway does $2M buy us', 'model the hiring plan's effect on burn', 'add a downside scenario if we miss plan by 30%', 'modelo financiero a 3 años', 'cuánto runway nos da la ronda', 'model financer i projeccions', 'quant de pista de despegament'. The engine is assumptions → revenue/cost/headcount builds → cash/burn/runway/scenarios, 18–36 months forward, every output a formula. NOT the next-13-weeks liquidity reconciled to the actual bank balance (that is finance-ops), NOT the isolated LTV/CAC math (that is unit-economics, which this model consumes as drivers), NOT the slide story (that is pitch-deck).",
1705
+ "description": "Use when a founder needs the driver-based 18–36 month projection: revenue, cost and headcount builds resolving to net burn, runway, cash-out date, scenarios and the raise size. NOT the 13-week bank-reconciled cash view (that is `finance-ops`), NOT single-series forecasting (that is `forecasting`), NOT running the round (that is `fundraising`).",
1706
1706
  "tags": [
1707
1707
  "financial-model",
1708
1708
  "projections",
@@ -1725,7 +1725,7 @@
1725
1725
  },
1726
1726
  {
1727
1727
  "id": "finetuning",
1728
- "description": "Use when adapting an open-weight model to a target FORM or BEHAVIOR — tone, output format, domain style, a reasoning/tool-calling pattern — via LoRA/QLoRA or full fine-tuning with TRL SFTTrainer and then preference optimization (DPO/ORPO/KTO/GRPO); when choosing fine-tune vs prompt vs RAG vs longer context; when setting LoRA r/alpha/target_modules; when eval loss climbs while train loss keeps falling; when the adapter seems to have learned nothing; when inference outputs garbage after a run. Triggers: 'fine-tune Llama/Qwen on our data', 'LoRA vs QLoRA vs full fine-tune', 'SFT then DPO/ORPO/KTO/GRPO', 'set target_modules / rank / alpha', 'my fine-tune overfits', 'the model forgot everything after training', 'write a GRPO reward function', 'ajustar un modelo amb LoRA amb les nostres dades'. NOT adding facts or fresh knowledge to a model (that is rag); NOT the fast single-GPU Unsloth backend or GGUF export (that is unsloth); NOT serving the trained model (that is vllm).",
1728
+ "description": "Use when adapting an open-weight model to a target form or behavior — tone, output format, reasoning pattern — via LoRA/QLoRA or full fine-tuning with TRL SFTTrainer, then preference optimization (DPO/ORPO/KTO/GRPO), and for fine-tune vs prompt vs RAG. NOT adding facts to a model (that is rag); NOT the single-GPU Unsloth backend or GGUF export (that is unsloth).",
1729
1729
  "tags": [
1730
1730
  "finetuning",
1731
1731
  "lora",
@@ -1749,7 +1749,7 @@
1749
1749
  },
1750
1750
  {
1751
1751
  "id": "firebase",
1752
- "description": "Use when building on Firebase — modeling Firestore data, writing or auditing Security Rules, wiring Auth, shipping Cloud Functions, storing files in Cloud Storage, or choosing modular Web/Admin SDK imports — and when symptoms appear like 'my database is open to the internet', 'rules reject my query', a counter stuck at 1 write/sec, custom-claim RBAC, or a callable function. Triggers: 'firestore.rules audit', 'denormalize for read paths', 'collection vs subcollection', 'my query works in the emulator but fails in prod with requires-an-index', 'set a custom admin claim', 'regles de seguretat de Firestore', 'mi base de datos está abierta a internet'. NOT managed-Postgres BaaS with SQL and RLS (that is supabase).",
1752
+ "description": "Use when building on Firebase — Firestore data modeling, Security Rules, Auth and custom claims, Cloud Functions, Storage, modular Web/Admin SDK imports — including symptoms like a database open to the internet, a query rejected by rules, or a doc stuck at ~1 write/sec. NOT managed-Postgres BaaS with SQL and RLS (that is supabase).",
1753
1753
  "tags": [
1754
1754
  "firebase",
1755
1755
  "firestore",
@@ -1783,7 +1783,7 @@
1783
1783
  },
1784
1784
  {
1785
1785
  "id": "fly-io",
1786
- "description": "Use when deploying or operating an app on Fly.io — writing fly.toml, running Machines across regions near users, attaching Volumes, managing secrets, or scaling (autostop/autostart, scale count, fly-replay). Triggers: 'fly deploy', 'fly launch', 'desplega a Fly.io a prop dels usuaris', 'users in Sydney see high latency but my app only runs in iad', 'scale my Fly app to zero overnight', 'attach a persistent disk to my Fly machine', 'forward writes to the primary region'. NOT a generic git-push PaaS (that is railway), NOT frontend edge hosting (that is vercel).",
1786
+ "description": "Use when deploying or operating an app on Fly.io — writing fly.toml, placing Machines in regions near users, attaching Volumes, managing secrets, or picking a scaling lever (autostop/autostart, scale count, fly-replay). NOT choosing which host to deploy on (that is `deployment`), NOT a git-push PaaS with no regions model (that is `railway`).",
1787
1787
  "tags": [
1788
1788
  "fly-io",
1789
1789
  "deployment",
@@ -1822,7 +1822,7 @@
1822
1822
  },
1823
1823
  {
1824
1824
  "id": "fundraising",
1825
- "description": "Use when planning or running an equity fundraising round and you need the STRATEGY and PROCESS, not a single document — sizing the raise to a milestone, choosing post-money SAFE vs priced round, building and tiering the investor target list around intro paths, sequencing outreach to manufacture momentum, managing the diligence pipeline, or reading a term sheet to know what to push on before you sign. Triggers: 'plan our seed raise', 'how much should we raise and on what terms', 'SAFE or priced round, what cap', 'build my investor target list', 'how many investors do I actually need to contact', 'we have meetings but no term sheet — the raise stalled', 'got a term sheet, what do I push back on', 'planifica la ronda seed', 'cuántos inversores tengo que contactar', '¿SAFE o ronda con precio?', 'planifiquem la ronda d'inversió'. NOT the slide-deck narrative (that is pitch-deck) and NOT the cap-table/valuation math (that is financial-model).",
1825
+ "description": "Use when planning or running an equity round as a process: sizing the raise to a milestone, choosing post-money SAFE vs priced round, tiering investors by intro path, sequencing outreach for momentum, or reading a term sheet before signing. NOT the slide narrative (that is `pitch-deck`), NOT the cap-table or valuation math (that is `financial-model`).",
1826
1826
  "tags": [
1827
1827
  "fundraising",
1828
1828
  "seed",
@@ -1845,7 +1845,7 @@
1845
1845
  },
1846
1846
  {
1847
1847
  "id": "game-design",
1848
- "description": "Use when designing or repairing the design of a game — inventing mechanics and systems, shaping the core loop, layering moment-to-moment / session / meta loops, planning progression, economy, currencies and sinks/faucets, tuning difficulty curves and balance, killing dominant strategies, adding 'juice'/game feel as design intent, prototyping and playtesting, or controlling scope, and especially when the ask is 'why isn't my game fun'. Triggers: 'design the core loop', 'my game isn't fun / doesn't hook', 'balance the economy', 'the difficulty curve feels off', 'players found a dominant strategy', 'how do I scope this', 'reward / progression loop', 'diseñar la mecánica', 'el juego no engancha', 'com equilibro l'economia'. NOT writing engine code, scripts or shaders (route to godot/unity/unreal), NOT narrative, dialogue or story beats (route to game-storytelling), NOT level layout or encounter placement (route to level-design).",
1848
+ "description": "Use when designing or repairing what makes a game fun — verbs and mechanics, moment-to-moment/session/meta loops, progression, economy faucets and sinks, difficulty curves, dominant strategies, juice, prototyping and scope. NOT engine code (that is `godot`), NOT story (that is `game-storytelling`), NOT level layout (that is `level-design`).",
1849
1849
  "tags": [
1850
1850
  "game-design",
1851
1851
  "mechanics",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ericrisco/rsc",
3
- "version": "0.1.48",
3
+ "version": "0.1.49",
4
4
  "description": "Eric Risco's agent-skills catalog as a granular, self-recommending CLI installer.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: domains-dns
3
- description: "Use when pointing a custom domain at a host, fixing broken HTTPS, choosing apex vs www, or publishing DNS rows — registering/delegating a name, writing A/AAAA/CNAME/ALIAS/MX/TXT/CAA/SRV records, standing up auto-renewing TLS, or cutting nameservers over without downtime. Triggers: 'point example.com at Vercel', 'set up DNS for the domain', 'NET::ERR_CERT_AUTHORITY_INVALID', 'CNAME flattening / ALIAS at the apex', 'dig +trace still shows the old IP', 'need a CAA record for Let's Encrypt', 'publish the SPF/DKIM/DMARC/MX rows', 'apuntar el dominio', 'el certificado caducó y no renueva', 'configurar els registres DNS'. NOT inbox placement / warmup / spam-rate tuning (that is email-deliverability), NOT what runs behind the name (that is deployment)."
3
+ description: "Use when pointing a domain at a host or fixing broken HTTPS — delegating nameservers, writing A/AAAA/CNAME/ALIAS/MX/TXT/CAA rows, apex vs www, standing up auto-renewing TLS, and cutting nameservers over without downtime. NOT inbox placement or warmup (that is `email-deliverability`), NOT what runs behind the name (that is `deployment`)."
4
4
  tags: [dns, tls, ssl, domains, https, acme, certbot, dig, caa, nameservers]
5
5
  recommends: [email-deliverability, deployment, monitoring, cloudflare, vercel]
6
6
  origin: risco
@@ -104,13 +104,7 @@ curl -vI https://example.com 2>&1 | grep -Ei 'HTTP/|location|SSL certificate' #
104
104
 
105
105
  Propagation is governed by **TTL, not a fixed 48 hours** (RFC 2181). To make a change land fast, lower the record's TTL (e.g. to 300s) *before* you change it, so resolvers re-query sooner. NS/registrar delegation changes can still lag because of registry/TLD TTLs.
106
106
 
107
- Verification checklist before you call it done:
108
-
109
- - [ ] `dig +short` returns the intended IP/host from a public resolver (`@1.1.1.1`).
110
- - [ ] Apex resolves via A/AAAA or ALIAS, **never** a CNAME.
111
- - [ ] `dig CAA` lists the CA that issued (or is empty — open policy).
112
- - [ ] `openssl s_client` shows the right subject, a complete chain, and `notAfter` comfortably ahead.
113
- - [ ] `curl -vI` returns 2xx/3xx with no cert warning; www and apex land on the one canonical host.
107
+ `scripts/verify.sh <domain>` runs the whole done-check read-only: apex-CNAME check, CAA sanity, chain completeness + expiry, HTTPS reachability, www↔apex canonical. The full dig/openssl/curl playbook and an error → cause → fix table are in `references/verify-and-debug.md`.
114
108
 
115
109
  ## Nameserver cutover (no downtime)
116
110
 
@@ -135,12 +129,3 @@ Order matters; each step has a reason.
135
129
  | Splitting one zone across two providers | Resolvers answer from whichever NS won; records vanish intermittently | One authoritative provider per zone |
136
130
  | TTL left at 86400 during migration | Old answer cached for a day after you flip | Drop to 300s a day ahead |
137
131
  | Testing certs against ACME production | Burns the 50-cert/7-day limit; week-long lockout | Use the staging endpoint first |
138
-
139
- ## References & siblings
140
-
141
- - `references/record-cookbook.md` — every record type with edge cases (TXT chunking, MX priority, SPF lookup limit, SRV, DNSSEC).
142
- - `references/tls-and-acme.md` — certbot/acme.sh, HTTP-01 vs DNS-01, wildcards, platform cert quirks, 2026 lifetimes + ARI.
143
- - `references/verify-and-debug.md` — full dig/openssl/curl playbook and error → cause → fix.
144
- - `scripts/verify.sh <domain>` — read-only audit: apex-CNAME check, CAA sanity, chain completeness + expiry, HTTPS reachability, www↔apex canonical.
145
-
146
- Route out when the ask isn't the records: inbox placement → ../email-deliverability/SKILL.md; what runs behind the name and how it's released → ../deployment/SKILL.md; platform-specific edge/build config → ../cloudflare/SKILL.md or ../vercel/SKILL.md.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: drizzle-orm
3
- description: "Use when modeling data or querying with Drizzle ORM in TypeScript — schema declared in `.ts`, type-safe `select`/`insert`/relational queries, and `drizzle-kit` migrations, or when choosing Drizzle over Prisma for an edge/serverless runtime. Triggers: 'set up Drizzle ORM', 'drizzle-kit generate', 'write a pgTable schema', '$inferSelect type', 'drizzle push vs generate for production', 'my db.query.posts.findMany returns no author relation', 'drizzle-kit conflicting migration', 'switch off Prisma to something lighter for edge', 'definir l esquema amb Drizzle i tipus type-safe', 'migrar de Prisma a Drizzle para algo más ligero', 'migració amb drizzle-kit'. NOT Prisma Client or schema.prisma (that is prisma-orm), NOT a generic zero-downtime migration strategy (that is db-migrations), NOT Postgres engine/indexing/EXPLAIN (that is postgresdb)."
3
+ description: "Use when modeling data or querying with Drizzle ORM in TypeScript — `pgTable` schema in `.ts`, type-safe select/insert/relational queries, `drizzle-kit` migrations. NOT Prisma Client or `schema.prisma` (that is `prisma-orm`), NOT ORM-agnostic migration strategy (that is `db-migrations`), NOT Postgres engine tuning or EXPLAIN (that is `postgresdb`)."
4
4
  tags: [drizzle, drizzle-orm, typescript-orm, drizzle-kit, sql, migrations, database, schema]
5
5
  recommends: [prisma-orm, postgresdb, db-migrations, neon, sqlite-turso, planetscale, supabase, typescript, sql]
6
6
  origin: risco
@@ -229,6 +229,3 @@ It checks a `drizzle()` call exists, the table helper matches the configured `di
229
229
  carries `dialect`/`schema`/`out`, that any `db.query` usage has `{ schema }`/`{ relations }` passed
230
230
  to `drizzle()`, and bans Prisma-isms (`schema.prisma`, `PrismaClient`, `prisma generate`) that mean
231
231
  the wrong ORM leaked in.
232
-
233
- For the full driver matrix and the v1↔v2 relations migration, see
234
- `references/relations-and-drivers.md`.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: duckdb
3
- description: "Use when you need fast analytical SQL over local files (Parquet/CSV/JSON/Arrow) with no server to run, when embedding OLAP in an app or notebook, when a pandas groupby/merge on multi-GB data is too slow, or when reading data straight from S3/HTTP/a lakehouse. Triggers: 'query a folder of parquet files in place', 'aggregate a 5GB CSV on my laptop', 'replace this slow pandas groupby', 'run SQL over a dataframe in Jupyter', 'read these parquet files from S3 without downloading', 'out-of-core aggregate', 'analizar un CSV enorme sin montar un servidor', 'consultar parquet en local sense base de dades'. NOT a multi-user production analytics server (that is clickhouse-analytics), NOT your app's transactional CRUD database (that is postgresdb)."
3
+ description: "Use when analytical SQL must run in-process with no server: Parquet/CSV/JSON/Arrow queried in place, OLAP embedded in an app or notebook, a slow pandas groupby on multi-GB data, or S3/lakehouse data read without downloading. NOT a multi-user analytics server (that is clickhouse-analytics), NOT an app's transactional CRUD store (that is postgresdb)."
4
4
  tags: [duckdb, olap, analytics, parquet, embedded-database, sql, columnar]
5
5
  recommends: [clickhouse-analytics, postgresdb, sql, sqlite-turso, data-cleaning, business-intelligence]
6
6
  origin: risco
@@ -34,8 +34,7 @@ for greenfield exploration.
34
34
  The two you will confuse most: DuckDB vs ClickHouse is **embedded-single-node vs server-distributed** —
35
35
  under ~10GB on one box DuckDB usually wins; pick ClickHouse when many people query concurrently. DuckDB vs
36
36
  SQLite is **same niche, opposite workload** — both embedded single-file, but SQLite is row-store OLTP and
37
- DuckDB is column-store OLAP. Don't run your app's writes through DuckDB. (Some recommended siblings above
38
- may not be built in this collection yet; the routing decision still holds.)
37
+ DuckDB is column-store OLAP. Don't run your app's writes through DuckDB.
39
38
 
40
39
  ## Install & version
41
40
 
@@ -175,22 +174,17 @@ escape hatch, not the default.
175
174
 
176
175
  ## Anti-patterns
177
176
 
178
- | Rationalization | Reality → STOP |
177
+ | Anti-pattern | Do instead |
179
178
  | --- | --- |
180
- | "Load the file into pandas, then query it" | You just paged the whole file through RAM. `FROM 'file.parquet' SELECT ...` scans only needed columns. |
181
- | "DuckDB can be our app's database" | One writer, single process — concurrent CRUD writers corrupt the workflow. App OLTP is `postgresdb`. |
182
- | "Spin up DuckDB behind our dashboard API for 200 users" | It's embedded, not a server; concurrent users serialize on the writer. That's `clickhouse-analytics`. |
183
- | "Just use the latest version in prod" | Pin the **LTS** (1.4.x; v1.4.4 shipped 2026-01-26) so a future upgrade doesn't break the on-disk format / extension ABI. |
184
- | "Download the S3 files, then read them" | `INSTALL httpfs` + `CREATE SECRET` reads `s3://...` in place with row-group pushdown. No download. |
185
- | "`SELECT *` over this 200-column Parquet" | Columnar engine — list the columns you need so it skips the rest. `SELECT *` reads everything. |
186
- | "GROUP BY a, b, c (restate every column)" | Drift bait. `GROUP BY ALL` tracks the SELECT automatically. |
187
- | "It's embedded so I don't need to think about RAM" | Set `memory_limit` / `threads`; without a cap a runaway aggregate can thrash before it spills. |
188
- | "Use DuckDB as our embeddings/vector store" | VSS exists but vector search is `vector-db`'s workflow, not DuckDB's home turf. |
189
-
190
- ## References
191
-
192
- - [references/python-and-interop.md](references/python-and-interop.md) — full Python client: connect/cursor, `?`/`$name` parameters, relational operators, register/unregister frames, pandas/Polars/Arrow/NumPy round-trips, transactions, threading caveat.
193
- - [references/remote-and-lakehouse.md](references/remote-and-lakehouse.md) — httpfs, `CREATE SECRET` for S3/GCS/Azure, hive globs, Iceberg/Delta/DuckLake reads, partitioned writes, MotherDuck `ATTACH 'md:'`.
179
+ | Loading a file into pandas, then querying the frame | Pages the whole file through RAM. `FROM 'file.parquet' SELECT ...` scans only needed columns. |
180
+ | Making DuckDB the app's database | One writer, single process — concurrent CRUD writers corrupt the workflow. App OLTP is `postgresdb`. |
181
+ | Serving a dashboard API for 200 users from DuckDB | It's embedded, not a server; concurrent users serialize on the writer. That's `clickhouse-analytics`. |
182
+ | Running the latest version in prod | Pin the **LTS** (1.4.x; v1.4.4 shipped 2026-01-26) so a future upgrade doesn't break the on-disk format / extension ABI. |
183
+ | Downloading the S3 files, then reading them | `INSTALL httpfs` + `CREATE SECRET` reads `s3://...` in place with row-group pushdown. No download. |
184
+ | `SELECT *` over a 200-column Parquet | Columnar engine — list the columns you need so it skips the rest. `SELECT *` reads everything. |
185
+ | `GROUP BY a, b, c` — restating every column | Drift bait. `GROUP BY ALL` tracks the SELECT automatically. |
186
+ | Treating "embedded" as "no need to think about RAM" | Set `memory_limit` / `threads`; without a cap a runaway aggregate can thrash before it spills. |
187
+ | Using DuckDB as the embeddings/vector store | VSS exists but vector search is `vector-db`'s workflow, not DuckDB's home turf. |
194
188
 
195
189
  ## Verify
196
190
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: dynamodb
3
- description: "Use when designing or operating an Amazon DynamoDB table - modeling entities and relationships without joins, choosing partition/sort keys, deciding single-table vs table-per-entity, adding a GSI/LSI, picking on-demand vs provisioned capacity, or diagnosing throttling. Triggers: 'design a DynamoDB table', 'model one-to-many / many-to-many without joins', 'single-table design worth it?', 'query the same data by two different keys' (inverted GSI), 'writes throttled even though I'm under my provisioned capacity' (hot partition), 'on-demand or provisioned?', 'why does my Query only return some rows?' (1 MB page), 'diseña la tabla DynamoDB para estos patrones de acceso', 'modela aquesta relació amb clau composta a DynamoDB'. NOT relational schema / SQL / EXPLAIN / B-tree indexing (that is postgresdb), and NOT document modeling with aggregation pipelines (that is mongodb)."
3
+ description: "Use when modeling or operating a DynamoDB table: deriving partition/sort keys from access patterns, single-table vs table-per-entity, adding a GSI/LSI, on-demand vs provisioned capacity, or diagnosing hot-partition throttling. NOT relational schema/SQL/EXPLAIN (that is `postgresdb`), NOT aggregation-pipeline document modeling (that is `mongodb`)."
4
4
  tags: [dynamodb, nosql, single-table-design, aws, data-modeling]
5
5
  recommends: [postgresdb, mongodb, redis, vector-db, aws-essentials, deployment, secure-coding]
6
6
  origin: risco
@@ -29,24 +29,16 @@ You **refuse** to: click through the AWS console, wire the IaC/CI provisioning p
29
29
  `../deployment/SKILL.md`), or model the data as normalized tables you then "join" in application code.
30
30
  You emit DDL, IaC snippets, and example SDK calls — you do not provision the infrastructure.
31
31
 
32
- ## The non-negotiable order
33
-
34
- Do these in sequence. Reordering them is the single most common DynamoDB mistake.
35
-
36
- 1. **Access patterns first.** You cannot query what your keys do not express, and there is no `WHERE`
37
- over arbitrary columns. List the patterns before you name a single attribute.
38
- 2. **Keys second.** Co-locate items that are read together so one `Query` returns them.
39
- 3. **Indexes last.** A GSI is a cost and a consistency compromise; reach for it only when the base
40
- table cannot answer a pattern.
41
- 4. **Capacity last.** Capacity mode is a billing decision, not a modeling one — it changes nothing about
42
- the keys. Pick it after the model is stable.
43
-
44
- Why the order matters: changing a table's key schema after launch means a full migration (new table +
45
- backfill + dual-write). Get the keys wrong and you pay for it forever.
32
+ The steps below run in sequence — access patterns, then keys, then indexes, then capacity. Reordering
33
+ them is the single most common DynamoDB mistake.
46
34
 
47
35
  ## Step 1 — Enumerate access patterns
48
36
 
49
- Produce this table before touching keys. It is also exactly what `scripts/verify.sh` checks.
37
+ You cannot query what your keys do not express, and there is no `WHERE` over arbitrary columns, so
38
+ produce this table before you name a single attribute. It is also exactly what `scripts/verify.sh`
39
+ checks: run `scripts/verify.sh <path-to-key-design.{md,json}>` and every row must carry a key target
40
+ (`pk`/`sk` or a named `gsi`) and a `query_type`. It FAILS on any pattern served by `Scan`, WARNS on
41
+ `FilterExpression`, and asserts ≤20 GSIs / ≤5 LSIs. Read-only; exits 0 on an empty or clean target.
50
42
 
51
43
  | pattern | entity | key condition | read/write | est. freq | served by |
52
44
  |---|---|---|---|---|---|
@@ -65,8 +57,12 @@ modeling smell — redesign the key, do not patch it with a filter.
65
57
 
66
58
  ## Step 2 — Key design + single-table decision
67
59
 
60
+ Get this wrong and you pay for it forever: changing a table's key schema after launch means a full
61
+ migration (new table + backfill + dual-write).
62
+
68
63
  **Composite-key encoding.** Use prefixed, sortable strings so one partition holds an entity and its
69
- children, and so a sort-key range query slices them:
64
+ children — items read together are co-located, so one `Query` returns them — and so a sort-key range
65
+ query slices them:
70
66
 
71
67
  ```text
72
68
  PK = USER#123
@@ -84,8 +80,10 @@ One `Query` with `PK = USER#123 AND SK begins_with("ORDER#")` returns all of tha
84
80
  already sorted. That is a **one-to-many** relationship with no join.
85
81
 
86
82
  **Many-to-many** uses an adjacency list: store the edge twice (or use an inverted GSI, see Step 3) so
87
- you can traverse it from either side. Full worked example in
88
- [references/access-patterns.md](references/access-patterns.md).
83
+ you can traverse it from either side. Worked end-to-end in
84
+ [references/access-patterns.md](references/access-patterns.md) — a multi-tenant SaaS / e-commerce model
85
+ (pattern table → PK/SK map → GSI map → AWS SDK v3 `@aws-sdk/lib-dynamodb` calls), adjacency-list
86
+ many-to-many, time-series and leaderboard patterns.
89
87
 
90
88
  **Single-table vs table-per-entity** — decide honestly, do not cargo-cult:
91
89
 
@@ -130,7 +128,8 @@ under one partition key). GSI-only tables have no such cap — a real reason to
130
128
 
131
129
  ## Step 4 — Capacity & cost
132
130
 
133
- Pick the mode after the model is stable. Decision table:
131
+ Capacity mode is a billing decision, not a modeling one — it changes nothing about the keys, so pick it
132
+ after the model is stable. Decision table:
134
133
 
135
134
  | traffic shape | recommendation | why |
136
135
  |---|---|---|
@@ -156,7 +155,8 @@ PK = METRIC#daily_total
156
155
  PK = METRIC#daily_total#<0..9>
157
156
  ```
158
157
 
159
- Capacity math, full pricing detail, and the sharding recipe live in
158
+ Capacity math, full pricing detail (including the Nov-2024 cut), warm throughput, the full quota table,
159
+ and hot-partition diagnosis + the sharding recipe live in
160
160
  [references/capacity-and-limits.md](references/capacity-and-limits.md).
161
161
 
162
162
  ## Step 5 — Operational guardrails
@@ -195,23 +195,10 @@ aws dynamodb query --table-name App \
195
195
  | Storing > 400 KB inline (images, docs, logs) | Hits the 400 KB item cap; bloats every read | Put the blob in S3, store the pointer in the item |
196
196
  | Picking capacity mode before the model is done | Mode is billing, not modeling — premature and often wrong | Stabilize keys/GSIs first, then choose on-demand vs provisioned |
197
197
 
198
- ## References & siblings
199
-
200
- - [references/access-patterns.md](references/access-patterns.md) — full multi-tenant SaaS / e-commerce
201
- model end-to-end: pattern table → PK/SK map → GSI map → example AWS SDK v3 (`@aws-sdk/lib-dynamodb`)
202
- calls; adjacency-list many-to-many; time-series and leaderboard patterns.
203
- - [references/capacity-and-limits.md](references/capacity-and-limits.md) — capacity-mode detail, current
204
- pricing + the Nov-2024 cut, warm throughput, the full quota table, hot-partition diagnosis + sharding.
205
-
206
- Route elsewhere when the engine is different: relational schema / SQL / EXPLAIN / indexing →
207
- `../postgresdb/SKILL.md`; MySQL-engine schema → `mysql`; document modeling + aggregation pipeline →
208
- `mongodb`; cache / queue / rate-limiter → `redis`; vector store / semantic search → `vector-db`; AWS
209
- account / IAM / VPC setup → `aws-essentials`; provisioning the table in CI/CD → `../deployment/SKILL.md`;
210
- API-layer auth/secrets in front of the table → `../secure-coding/SKILL.md`.
211
-
212
- ## verify.sh
198
+ ## Route elsewhere when the engine is different
213
199
 
214
- `scripts/verify.sh <path-to-key-design.{md,json}>` checks the access-pattern artifact: every row has a
215
- key target (`pk`/`sk` or a named `gsi`) and a `query_type`; it FAILS on any pattern served by `Scan`,
216
- WARNS on `FilterExpression`, and asserts ≤20 GSIs / ≤5 LSIs. Read-only; exits 0 on an empty or clean
217
- target.
200
+ Relational schema / SQL / EXPLAIN / indexing → `../postgresdb/SKILL.md`; MySQL-engine schema → `mysql`;
201
+ document modeling + aggregation pipeline → `mongodb`; cache / queue / rate-limiter → `redis`; vector
202
+ store / semantic search → `vector-db`; AWS account / IAM / VPC setup → `aws-essentials`; provisioning the
203
+ table in CI/CD → `../deployment/SKILL.md`; API-layer auth/secrets in front of the table →
204
+ `../secure-coding/SKILL.md`.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: e-signature
3
- description: "Use when adding legally-valid electronic-signature flows to documents — sending a PDF or template for signature, embedding signing in your own app, tracking status, verifying signing webhooks, and retrieving the signed PDF plus its audit trail via DocuSign or Dropbox Sign. Triggers: 'send this contract for signature', 'create a DocuSign envelope', 'signature_request/send', 'embed signing in our app', 'notify us when it's signed', 'set up DocuSign JWT auth', 'is a typed-name signature legally binding?', 'do we need QES for EU customers?', 'enviar a firmar', 'firma electrònica amb validesa legal'. NOT drafting the contract text itself (that is contracts) and NOT OCR/parsing/extracting fields from documents (that is document-processing)."
3
+ description: "Use when wiring an e-signature flow with DocuSign or Dropbox Sign — picking the SES/AES/QES legal tier, sending a PDF or template for signature, embedded signing, verifying signing webhooks, retrieving the signed PDF plus audit trail. NOT drafting contract text (that is `contracts`), NOT extracting fields from PDFs (that is `document-processing`)."
4
4
  tags: [e-signature, docusign, dropbox-sign, esignature-api, webhooks, eidas, esign-act, audit-trail]
5
5
  recommends: [contracts, document-processing, webhooks, api-connector-builder, gdpr-privacy, automation-flows]
6
6
  origin: risco
@@ -10,17 +10,13 @@ origin: risco
10
10
 
11
11
  You are wiring a third-party signing API (DocuSign eSignature or Dropbox Sign, ex-HelloSign) into someone's app or backend. You take a PDF or template, define signers and fields, send it for signature, track status, react to completion via a verified webhook, and retrieve the signed PDF plus its audit trail.
12
12
 
13
- You are NOT writing the contract language — clauses, indemnity, liability go to `../contracts/SKILL.md`. You are NOT doing OCR, field extraction, or PDF splitting with no signing involved — that is `../document-processing/SKILL.md`. You only build the signing flow.
14
-
15
- ## Three rules
16
-
17
- 1. **Pick the legal tier deliberately, before you write code.** A typed name (SES) is binding for most B2B in the US and EU, but a high-stakes EU document may need AES or QES. Choosing the tier decides which provider features you enable (ID Verification, SMS/access code, qualified signature). Get this wrong and the signature is hard to defend in court.
18
- 2. **Sandbox / `test_mode` before prod, always.** A live send is billable and emails a real human. DocuSign demo env is `https://demo.docusign.net`; Dropbox Sign uses `test_mode: 1`. Only non-test sends count against quota and reach signers.
19
- 3. **The deliverable is the signed PDF + audit trail, retrieved and stored.** A send is not done when status is `sent` — it is done when the signer completes and you have pulled the signed document AND its evidence (DocuSign Certificate of Completion, Dropbox Sign audit-trail PDF). Fire-and-forget is the most common bug here.
13
+ Contract language — clauses, indemnity, liability — goes to `../contracts/SKILL.md`; OCR, field extraction, or PDF splitting with no signing involved is `../document-processing/SKILL.md`.
20
14
 
21
15
  ## Decision: which legal tier do you need?
22
16
 
23
- US law (ESIGN Act + UETA) has **no tiers** — e-signatures equal wet ink. The EU (eIDAS, and eIDAS 2.0 / Reg (EU) 2024/1183 in force since May 2024) defines three. Map the document's stakes to a tier, then to a provider feature.
17
+ Pick the tier deliberately, **before you write code**: it decides which provider features you enable (ID Verification, SMS/access code, qualified signature), and getting it wrong leaves a signature that is hard to defend in court.
18
+
19
+ US law (ESIGN Act + UETA) has **no tiers** — e-signatures equal wet ink. The EU (eIDAS, and eIDAS 2.0 / Reg (EU) 2024/1183 in force since May 2024) defines three. A typed name (SES) is binding for most B2B in the US and EU, but a high-stakes EU document may need AES or QES. Map the document's stakes to a tier, then to a provider feature.
24
20
 
25
21
  | Tier | What it is | When you need it | Provider feature to enable |
26
22
  |------|-----------|------------------|----------------------------|
@@ -47,6 +43,8 @@ Either is fine. Pick one and stay on it so you keep **one** consistent audit tra
47
43
 
48
44
  Never commit keys. Read everything from env; the RSA private key lives in a secret store or a file path, never inline in source.
49
45
 
46
+ Point at the sandbox before prod, always — a live send is billable and emails a real human. DocuSign demo env is `https://demo.docusign.net`; Dropbox Sign uses `test_mode: 1`. Only non-test sends count against quota and reach signers.
47
+
50
48
  | Env var | Provider | Holds |
51
49
  |---------|----------|-------|
52
50
  | `DOCUSIGN_INTEGRATION_KEY` | DocuSign | client/integration key (GUID) |
@@ -135,6 +133,8 @@ const res = await api.signatureRequestSend({
135
133
 
136
134
  ## Webhooks / completion
137
135
 
136
+ A send is not done when status is `sent` — it is done when the signer completes and you have pulled the signed document AND its evidence (DocuSign Certificate of Completion, Dropbox Sign audit-trail PDF), retrieved and stored. Fire-and-forget is the most common bug here.
137
+
138
138
  **Verify the signature before you trust anything in the payload.** The body is attacker-controllable until you have verified it.
139
139
 
140
140
  - **DocuSign Connect**: HMAC-signed; verify the `X-DocuSign-Signature-1` header against the raw request body using your Connect HMAC key.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: electron
3
- description: "Use when building, hardening, or shipping a cross-platform desktop app with Electron — wiring the main/renderer/preload process model, exposing OS capabilities to a web UI over typed IPC, locking down an insecure shell, or packaging with code signing and auto-update. Triggers: 'build a desktop app for Mac/Windows/Linux from my web app', 'let my renderer read a file safely', 'audit my Electron security (nodeIntegration, sandbox, CSP)', 'my preload exposes ipcRenderer and the renderer can require(\"fs\")', 'Squirrel.Mac won\\'t auto-update my app', 'migrate off @electron/remote and BrowserView', 'empaquetar mi app de escritorio para Windows y Mac con firma de código y auto-actualización', 'empaquetar app d\\'escriptori amb auto-actualització'. NOT a smaller Rust-backed shell on the OS native webview (that is tauri)."
3
+ description: "Use when building, hardening, or shipping a cross-platform Electron desktop app — main/renderer/preload process model, typed contextBridge IPC, locking down nodeIntegration/contextIsolation/sandbox/CSP, or packaging with signing and auto-update. NOT a Rust-backed shell on the native webview (that is tauri), nor the web UI inside it (that is react)."
4
4
  tags: [electron, desktop, ipc, security, packaging, auto-update, code-signing]
5
5
  recommends: [tauri, react, nodejs, github-actions, secure-coding]
6
6
  origin: risco
@@ -8,13 +8,9 @@ origin: risco
8
8
 
9
9
  # Electron — desktop shell, typed IPC, hardening, signing
10
10
 
11
- > Build, harden, and ship cross-platform desktop apps. Treat the renderer as hostile
12
- > territory and the IPC channel as an attack surface — that is the dominant failure mode
13
- > in real Electron apps, so the whole skill is organized around it.
14
-
15
11
  This skill owns the **desktop shell**: process model, IPC, security, packaging, signing,
16
- auto-update. It does **not** own the web UI inside the window (`../react/SKILL.md` if it
17
- existed), the Node backend logic, or the CI runner matrix.
12
+ auto-update. It does **not** own the web UI inside the window (`../react/SKILL.md`), the
13
+ Node backend logic, or the CI runner matrix.
18
14
 
19
15
  ## The mental model — three processes, one rule
20
16
 
@@ -28,7 +24,8 @@ wrong one is the root cause of most security holes.
28
24
  | preload | isolated world, runs before page JS | semi-trusted | window | the **only** bridge: `contextBridge` exposes a tiny API |
29
25
 
30
26
  **The governing rule: the renderer is untrusted, the main process holds all privilege, and
31
- the preload is the only sanctioned bridge between them.** Everything below is a corollary.
27
+ the preload is the only sanctioned bridge between them.** Renderer-to-OS escalation is the
28
+ dominant failure mode in real Electron apps, so everything below is a corollary.
32
29
 
33
30
  ## Start right
34
31
 
@@ -55,7 +52,7 @@ src/
55
52
  ipc/types.ts # IPC contract shared by main + preload
56
53
  ```
57
54
 
58
- ## The security baseline — non-negotiable
55
+ ## The security baseline
59
56
 
60
57
  Modern Electron defaults are already secure (`nodeIntegration:false`,
61
58
  `contextIsolation:true`, `sandbox:true` since Electron 20). **Assert them explicitly anyway**
@@ -185,32 +182,23 @@ Authenticode, and `electron-updater` + GitHub Releases wiring: `references/packa
185
182
  The CI matrix that *runs* these builds across three OSes is `github-actions`' job; this skill
186
183
  defines *what* to build and sign.
187
184
 
188
- ## Migration smells — and the fix
189
-
190
- | Smell in the code | Why it's dangerous | Fix |
191
- |--------------------------------------------|---------------------------------------------|--------------------------------------------------|
192
- | `nodeIntegration: true` | Page JS gets `require('fs')`, full Node | Set `false`; move the capability behind IPC |
193
- | `contextIsolation: false` | Page can rewrite the preload's globals | Set `true` (default) |
194
- | `@electron/remote` import | Sync main-object access = renderer→main RCE | Replace with explicit `ipcMain.handle` channels |
195
- | `new BrowserView(...)` | Deprecated since 30, will be removed | `new WebContentsView(...)` |
196
- | `exposeInMainWorld('api', ipcRenderer)` | Universal IPC weapon (empty object now) | One function per channel |
197
-
198
- ## Anti-patterns
185
+ ## Anti-patterns and migration smells
199
186
 
200
187
  | Anti-pattern | Why it's wrong | Do instead |
201
188
  |-------------------------------------------------------|------------------------------------------------------------|-------------------------------------------------------|
202
- | `nodeIntegration: true` | Any XSS in the renderer becomes OS-level RCE | `false` + IPC for every privileged op |
203
- | `contextIsolation: false` | Renderer can tamper with preload internals | `true` (the default) |
189
+ | `nodeIntegration: true` | Page JS gets `require('fs')`; any XSS becomes OS-level RCE | `false`; move the capability behind IPC |
190
+ | `contextIsolation: false` | Page can rewrite the preload's globals | `true` (the default) |
204
191
  | `sandbox: false` without a reason | Drops the OS sandbox around the renderer | `true`; only relax for a measured, isolated need |
205
- | Exposing `ipcRenderer`/its methods over the bridge | Page can call any channel with any payload | One typed function per channel |
192
+ | `exposeInMainWorld('api', ipcRenderer)` or its methods | Universal IPC weapon — any channel, any payload (and an empty object now) | One typed function per channel |
206
193
  | No arg validation in `ipcMain.handle` | Renderer is an untrusted client; you trust its input | Type-check + bound every arg before acting |
207
194
  | `webSecurity: false` to "fix CORS" | Disables same-origin policy app-wide | Keep `true`; proxy/handle CORS in main |
208
195
  | CSP only in a `<meta>` tag | Doesn't cover the initial document; bypassable | Set CSP in `onHeadersReceived` |
209
196
  | Loading a remote URL into a Node-enabled window | Remote site runs with your app's privilege | Bundle UI locally; sandbox + nav lockdown for remote |
197
+ | `@electron/remote` import | Sync main-object access = renderer→main RCE | Replace with explicit `ipcMain.handle` channels |
198
+ | `new BrowserView(...)` | Deprecated since Electron 30, will be removed | `new WebContentsView(...)` |
210
199
  | Auto-update with an unsigned/un-notarized build | Squirrel.Mac silently refuses; no updates ship | Sign + notarize (mac), Authenticode (win) first |
211
200
  | Shipping on an EOL Electron major | Unpatched Chromium CVEs in your users' hands | Stay within the latest 3 majors |
212
201
  | Heavy CPU work in the main process | Blocks the event loop → the whole UI freezes | `utilityProcess`/worker, or do it in the renderer |
213
- | Keeping `@electron/remote` | A known renderer→main escalation path | Migrate to explicit IPC |
214
202
 
215
203
  ## Verify
216
204