@ericrisco/rsc 0.1.47 → 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 +29 -29
- package/package.json +1 -1
- package/skills/db-migrations/SKILL.md +3 -20
- package/skills/decision-records/SKILL.md +3 -3
- package/skills/deep-learning/SKILL.md +12 -49
- package/skills/digitalocean/SKILL.md +10 -34
- package/skills/django/SKILL.md +2 -7
- package/skills/docker/SKILL.md +12 -44
- package/skills/document-processing/SKILL.md +5 -13
- package/skills/domains-dns/SKILL.md +2 -17
- package/skills/drizzle-orm/SKILL.md +1 -4
- package/skills/duckdb/SKILL.md +12 -18
- package/skills/dynamodb/SKILL.md +27 -40
- package/skills/e-signature/SKILL.md +9 -9
- package/skills/e2e-testing/SKILL.md +2 -12
- package/skills/electron/SKILL.md +12 -24
- package/skills/elixir/SKILL.md +6 -14
- package/skills/email-connector/SKILL.md +20 -37
- package/skills/email-deliverability/SKILL.md +9 -29
- package/skills/embeddings-search/SKILL.md +10 -19
- package/skills/error-handling/SKILL.md +10 -44
- package/skills/error-handling/scripts/verify.sh +0 -0
- package/skills/expo/SKILL.md +30 -45
- package/skills/fal/SKILL.md +2 -16
- package/skills/finance-ops/SKILL.md +5 -13
- package/skills/financial-model/SKILL.md +17 -35
- package/skills/finetuning/SKILL.md +7 -19
- package/skills/firebase/SKILL.md +19 -33
- package/skills/fly-io/SKILL.md +4 -13
- package/skills/fundraising/SKILL.md +5 -14
- package/skills/game-design/SKILL.md +9 -33
package/skills/django/SKILL.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: django
|
|
3
|
-
description: "Use when building, reviewing, securing, testing or shipping a Django
|
|
3
|
+
description: "Use when building, reviewing, securing, testing or shipping a Django app — models, migrations, QuerySets/managers, FBV/CBV views, forms, the admin, settings split, and Django REST Framework (serializers, ModelViewSet, permissions). NOT async FastAPI/Pydantic services (that is `fastapi`), NOT Postgres schema/index work (that is `postgresdb`)."
|
|
4
4
|
tags: [python, django, orm, drf, backend, web]
|
|
5
5
|
recommends: [postgresdb, secure-coding, deployment, testing-py, api-design]
|
|
6
6
|
origin: risco
|
|
@@ -31,10 +31,7 @@ add a dependency.
|
|
|
31
31
|
|
|
32
32
|
**Version rule:** default to 5.2 LTS. Pick 6.0 only for a concrete Tasks/CSP need, and say so.
|
|
33
33
|
|
|
34
|
-
##
|
|
35
|
-
|
|
36
|
-
The test: if the project has `manage.py` and `INSTALLED_APPS`, it is Django. If it imports
|
|
37
|
-
`fastapi` + `pydantic`, it is not.
|
|
34
|
+
## Route elsewhere
|
|
38
35
|
|
|
39
36
|
| Situation | Route to |
|
|
40
37
|
|---|---|
|
|
@@ -45,8 +42,6 @@ The test: if the project has `manage.py` and `INSTALLED_APPS`, it is Django. If
|
|
|
45
42
|
| REST contract design (cursor vs offset, status codes, versioning) | `api-design` |
|
|
46
43
|
| ruff/uv/general type hints, packaging | `python` |
|
|
47
44
|
|
|
48
|
-
Everything else Django-shaped — models, QuerySets, migrations, views, DRF, settings — is here.
|
|
49
|
-
|
|
50
45
|
## Project shape
|
|
51
46
|
|
|
52
47
|
Split settings by environment; never ship one `settings.py` toggled by `DEBUG`.
|
package/skills/docker/SKILL.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: docker
|
|
3
|
-
description: "Use when authoring or auditing a Dockerfile
|
|
3
|
+
description: "Use when authoring or auditing a Dockerfile, shrinking a bloated image, hardening a container that runs as root, picking a base image, or wiring a Compose dev loop with hot reload. NOT CI builds or deploy-to-host (that is `deployment`), NOT k8s autoscaling (that is `scaling`), NOT app-level injection or secrets-in-code (that is `secure-coding`)."
|
|
4
4
|
tags: [docker, containers, dockerfile, compose, image-security]
|
|
5
5
|
recommends: [deployment, secure-coding, scaling, github-actions]
|
|
6
6
|
origin: risco
|
|
@@ -20,17 +20,8 @@ source → Dockerfile (multi-stage, cache mounts) → small + scanned image
|
|
|
20
20
|
```
|
|
21
21
|
|
|
22
22
|
The hand-off is sharp: **the moment you have a green, scanned image and a working
|
|
23
|
-
`compose.yaml`, you are done.**
|
|
24
|
-
|
|
25
|
-
image-internals specialist it leans on.
|
|
26
|
-
|
|
27
|
-
## When to use
|
|
28
|
-
|
|
29
|
-
- Authoring or auditing a `Dockerfile` for any stack (Node, Python, Go, Rust, JVM, static SPA).
|
|
30
|
-
- An image is too big (800 MB+ Node image), builds too slowly, or fails `trivy`/`hadolint`.
|
|
31
|
-
- A container runs as root and you need least privilege.
|
|
32
|
-
- Standing up local dev with `compose.yaml` + `docker compose watch` (sync / rebuild / sync+restart).
|
|
33
|
-
- Choosing a base image and getting multi-stage + BuildKit cache mounts right.
|
|
23
|
+
`compose.yaml`, you are done.** Everything downstream is `../deployment/SKILL.md` — it already
|
|
24
|
+
references Dockerfiles and leans on you for the image internals.
|
|
34
25
|
|
|
35
26
|
## When NOT to use — and who owns it instead
|
|
36
27
|
|
|
@@ -42,24 +33,6 @@ image-internals specialist it leans on.
|
|
|
42
33
|
| Install/operate a self-hosted PaaS | `../coolify/SKILL.md` | Host operation, not the image |
|
|
43
34
|
| Containerless PaaS deploy mechanics | `../railway/SKILL.md`, `../fly-io/SKILL.md` | Platform deploy, not the Dockerfile |
|
|
44
35
|
|
|
45
|
-
## The five rules
|
|
46
|
-
|
|
47
|
-
1. **Multi-stage, always.** Build deps (compilers, dev packages, the full SDK) must never reach
|
|
48
|
-
the runtime image. Multiple `FROM` stages; copy only the built artifact into a minimal final
|
|
49
|
-
stage. *Why: a Go binary needs ~10 MB, not a 900 MB toolchain — and every extra package is CVE
|
|
50
|
-
surface.*
|
|
51
|
-
2. **Choose the base deliberately and pin it — never `:latest`.** Pin a tag (ideally a digest)
|
|
52
|
-
so a rebuild is reproducible. *Why: `:latest` silently changes under you, breaking builds and
|
|
53
|
-
busting the scan you just passed.*
|
|
54
|
-
3. **Install deps before copying source.** Order layers so the lockfile install is cached and use
|
|
55
|
-
BuildKit cache mounts. *Why: copying everything first invalidates the dependency layer on every
|
|
56
|
-
one-character source edit.*
|
|
57
|
-
4. **Run as a non-root `USER`.** Default PID 1 is root (UID 0); a container escape from root is a
|
|
58
|
-
host compromise. *Why: least privilege is the cheapest blast-radius reduction you can ship.*
|
|
59
|
-
5. **One concern per container; EXEC-form `CMD`; add a `HEALTHCHECK`.** *Why: shell-form `CMD foo`
|
|
60
|
-
wraps your process in `/bin/sh -c`, which swallows `SIGTERM` so the container ignores graceful
|
|
61
|
-
shutdown; a healthcheck lets Compose/orchestrators gate on readiness.*
|
|
62
|
-
|
|
63
36
|
## Pick a base image (2026 reality)
|
|
64
37
|
|
|
65
38
|
| Base | Size | CVE / patch velocity | Debuggable? | Wins when |
|
|
@@ -72,7 +45,8 @@ image-internals specialist it leans on.
|
|
|
72
45
|
|
|
73
46
|
Rule of thumb: glibc app with native deps → `*-slim` or Wolfi. Static Go binary → `scratch` or
|
|
74
47
|
`distroless/static`. Security mandate → Chainguard. Reach for `alpine` only when you've confirmed
|
|
75
|
-
your wheels/native libs build against musl. Per-language tag maps
|
|
48
|
+
your wheels/native libs build against musl. Per-language tag maps, multi-stage templates for the
|
|
49
|
+
stacks not shown below (Rust, JVM `jlink`, static SPA → nginx) and multi-arch buildx live in
|
|
76
50
|
[references/base-images-and-stages.md](references/base-images-and-stages.md).
|
|
77
51
|
|
|
78
52
|
## Multi-stage skeletons
|
|
@@ -204,8 +178,9 @@ docker build --secret id=npm_token,env=NPM_TOKEN .
|
|
|
204
178
|
## Compose for dev
|
|
205
179
|
|
|
206
180
|
Canonical filename is `compose.yaml`; **omit the obsolete `version:` key**; use `docker compose`
|
|
207
|
-
(v2 subcommand), never the standalone `docker-compose` v1.
|
|
208
|
-
|
|
181
|
+
(v2 subcommand), never the standalone `docker-compose` v1. One concern per container — the app, the
|
|
182
|
+
database and the seed job are separate services. `develop.watch` is GA since Compose 2.22.0 and
|
|
183
|
+
gives you a tight dev loop.
|
|
209
184
|
|
|
210
185
|
```yaml
|
|
211
186
|
services:
|
|
@@ -264,20 +239,13 @@ These are exactly what `scripts/verify.sh` automates (it skips a tool gracefully
|
|
|
264
239
|
|
|
265
240
|
| Bad | Good | Why |
|
|
266
241
|
|---|---|---|
|
|
267
|
-
| `FROM node:latest` | `FROM node:24-bookworm-slim
|
|
268
|
-
| runs as root (no `USER`) | `USER nonroot` / `USER 10001` | root escape == host compromise |
|
|
242
|
+
| `FROM node:latest` | `FROM node:24-bookworm-slim`, or pin a digest | `:latest` drifts under you; builds + scans become non-reproducible |
|
|
243
|
+
| runs as root (no `USER`) | `USER nonroot` / `USER 10001` | PID 1 defaults to root (UID 0), so a container escape == host compromise |
|
|
269
244
|
| `ARG TOKEN` / `ENV SECRET=` | `RUN --mount=type=secret,...` | ARG/ENV persist in image history & layers |
|
|
270
245
|
| `COPY . .` then `RUN npm ci` | copy lockfile → install → copy source | source edits bust the dep cache layer every build |
|
|
271
246
|
| single `FROM`, ship the SDK | multi-stage, copy only the artifact | runtime carries compilers & dev CVE surface |
|
|
272
247
|
| `apt-get install x` | `apt-get install --no-install-recommends x && rm -rf /var/lib/apt/lists/*` | recommends + apt lists bloat the layer |
|
|
273
248
|
| no `.dockerignore` | mirror `.gitignore` + build outputs | whole context ships; `.env`/`.git` can leak |
|
|
274
249
|
| `version: "3.8"` / `docker-compose` v1 | omit `version:`; `docker compose` v2 | the key is obsolete; v1 is EOL |
|
|
275
|
-
| `CMD npm start` (shell form) | `CMD ["node", "server.js"]` (exec) | shell form swallows `SIGTERM
|
|
276
|
-
|
|
277
|
-
## References & siblings
|
|
278
|
-
|
|
279
|
-
- [references/base-images-and-stages.md](references/base-images-and-stages.md) — per-language
|
|
280
|
-
multi-stage templates (Rust, JVM jlink, static SPA → nginx), tag maps, multi-arch buildx.
|
|
281
|
-
- [references/compose-recipes.md](references/compose-recipes.md) — fuller Compose patterns.
|
|
282
|
-
- Once the image is green and scanned: `../deployment/SKILL.md`. Image/container hardening pairs
|
|
283
|
-
with `../secure-coding/SKILL.md`. Self-hosted host ops: `../coolify/SKILL.md`.
|
|
250
|
+
| `CMD npm start` (shell form) | `CMD ["node", "server.js"]` (exec) | shell form wraps the process in `/bin/sh -c`, which swallows `SIGTERM` — no graceful shutdown |
|
|
251
|
+
| no `HEALTHCHECK` | `HEALTHCHECK CMD [...]` | nothing for Compose/orchestrators to gate readiness on (`condition: service_healthy`) |
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: document-processing
|
|
3
|
-
description: "Use when
|
|
3
|
+
description: "Use when the deliverable is a document's bytes or its literal content — text/tables out of PDFs, AcroForm fill and flatten, page merge/split, PDF/DOCX from templates, OCR of image-only scans. NOT schema-typed fields pulled from text (that is structured-extraction), NOT signature routing (e-signature) or spreadsheet cells/formulas (spreadsheet-ops)."
|
|
4
4
|
tags: [pdf, ocr, docx, forms, extraction, document-ai]
|
|
5
5
|
recommends: [structured-extraction, e-signature, spreadsheet-ops, rag, data-scraper]
|
|
6
6
|
origin: risco
|
|
@@ -15,7 +15,7 @@ The boundary test, apply it first:
|
|
|
15
15
|
- Deliverable is **raw text / Markdown / table cells / a generated file** → you are in the right place.
|
|
16
16
|
- Deliverable is **a typed object matching a schema** (`{parties: [...], total: 1234.50}`) → that is `structured-extraction`. This skill stops at "clean Markdown out of the file"; the schema-constrained extraction runs on that Markdown.
|
|
17
17
|
|
|
18
|
-
Everything else routes too: signing → `e-signature`, spreadsheet grids/formulas → `spreadsheet-ops`, indexing for Q&A → `rag
|
|
18
|
+
Everything else routes too: signing with an audit trail → `e-signature`, spreadsheet grids/formulas/XLSX-as-data → `spreadsheet-ops`, indexing for cross-document Q&A → `rag` (this skill *produces* the text `rag` ingests, it does not index it), downloading the files off a site → `data-scraper`.
|
|
19
19
|
|
|
20
20
|
## Step 0 — does the PDF have a text layer?
|
|
21
21
|
|
|
@@ -42,7 +42,7 @@ If `extract_text()` returns empty (or near-empty) across the first few pages, it
|
|
|
42
42
|
| Goal | Use | Why |
|
|
43
43
|
|---|---|---|
|
|
44
44
|
| Extract text + tables with layout | **pdfplumber** | Layout-aware; `extract_tables()` returns rows/cols as Python lists → pandas/CSV. |
|
|
45
|
-
| Raw text, merge, split, rotate, page ops | **pypdf** (6.12.2) | Pure-Python, no C deps, runs in Lambda/containers; the maintained core. |
|
|
45
|
+
| Raw text, merge, split, rotate, page ops | **pypdf** (6.12.2) | Pure-Python, no C deps, runs in Lambda/containers; the maintained core — import `pypdf`, never the dead `PyPDF2`, which was merged back into it. |
|
|
46
46
|
| Fill an interactive PDF form | **pypdf** | `update_page_form_field_values` writes AcroForm fields; can flatten. |
|
|
47
47
|
| Generate a Word/DOCX from a template | **docxtpl** (0.20.x) | A real `.docx` becomes a Jinja2 template; author in Word, tag, render. |
|
|
48
48
|
| Generate a PDF from scratch | **ReportLab** | Canvas / Platypus flowables for laid-out PDFs. |
|
|
@@ -50,8 +50,6 @@ If `extract_text()` returns empty (or near-empty) across the first few pages, it
|
|
|
50
50
|
| OCR messy scans / handwriting / hard tables, API ok | **Mistral OCR** | `mistral-ocr-2512` (OCR 3), ~$2 / 1,000 pages, tuned for forms + handwriting. |
|
|
51
51
|
| Fastest extract / easiest page→PNG raster | **PyMuPDF** ⚠️ **AGPL** | Fast, but AGPL: shipping it imposes an open-source obligation or needs a paid license. Flag this before recommending. |
|
|
52
52
|
|
|
53
|
-
Rule: use the maintained **`pypdf`** import, never the dead `PyPDF2` — `PyPDF2` is unmaintained and was merged back into `pypdf`. Importing it is a signal of stale code.
|
|
54
|
-
|
|
55
53
|
## Extraction recipes
|
|
56
54
|
|
|
57
55
|
Text + tables with `pdfplumber`, straight to CSV:
|
|
@@ -128,7 +126,7 @@ with open("filled.pdf", "wb") as f:
|
|
|
128
126
|
writer.write(f)
|
|
129
127
|
```
|
|
130
128
|
|
|
131
|
-
|
|
129
|
+
`auto_regenerate` defaults to `True` for legacy reasons, and you almost never want it. Checkbox/radio values are the field's `/V` on-state (often `/Yes`), not `True` — read the field to find it.
|
|
132
130
|
|
|
133
131
|
## Generation
|
|
134
132
|
|
|
@@ -191,16 +189,10 @@ markdown = "\n\n".join(p.markdown for p in resp.pages)
|
|
|
191
189
|
|
|
192
190
|
**Never trust OCR output blind.** OCR confuses `0/O`, `1/l/I`, and drops or shifts decimal points — a `1.234,50` can come back as `1234,50` or `1,234.50`. Always spot-check totals, dates, and ID numbers against the rendered page before you hand the text downstream. For clean scans with no budget, plain `pytesseract` is the zero-cost baseline, but it is weak on layout/tables versus the pipelines above.
|
|
193
191
|
|
|
194
|
-
## Scale
|
|
192
|
+
## Scale
|
|
195
193
|
|
|
196
194
|
Batch jobs: parallelize per-file, cap concurrency on the hosted API (rate limits + cost), and use Mistral's Batch API for the 50% discount on large runs. Engine install matrix, exact version pins, the full licensing table, the Docling-vs-Marker-vs-Mistral feature/cost comparison, and troubleshooting (encrypted PDFs, mangled AcroForm field names, multi-column reading order, CJK/handwriting) live in `references/engines.md` — read it before a non-trivial install.
|
|
197
195
|
|
|
198
|
-
Handoffs:
|
|
199
|
-
- Need typed fields (`{total, due_date, parties}`) out of the Markdown you produced → `structured-extraction`.
|
|
200
|
-
- Need to route the finished PDF for signature with an audit trail → `e-signature`.
|
|
201
|
-
- The grid is really a spreadsheet (cells, formulas, XLSX as data) → `spreadsheet-ops`.
|
|
202
|
-
- Need to index the extracted text for cross-document Q&A → `rag`. This skill *produces* the text `rag` ingests; it does not index it.
|
|
203
|
-
|
|
204
196
|
## Anti-patterns
|
|
205
197
|
|
|
206
198
|
| Anti-pattern | Why it is wrong | Do instead |
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: domains-dns
|
|
3
|
-
description: "Use when pointing a
|
|
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
|
-
|
|
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
|
|
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`.
|
package/skills/duckdb/SKILL.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: duckdb
|
|
3
|
-
description: "Use when
|
|
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.
|
|
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
|
-
|
|
|
177
|
+
| Anti-pattern | Do instead |
|
|
179
178
|
| --- | --- |
|
|
180
|
-
|
|
|
181
|
-
|
|
|
182
|
-
|
|
|
183
|
-
|
|
|
184
|
-
|
|
|
185
|
-
|
|
|
186
|
-
|
|
|
187
|
-
| "
|
|
188
|
-
|
|
|
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
|
|
package/skills/dynamodb/SKILL.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: dynamodb
|
|
3
|
-
description: "Use when
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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.
|
|
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
|
-
|
|
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,
|
|
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
|
-
##
|
|
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
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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: e2e-testing
|
|
3
|
-
description: "Use when writing or stabilizing
|
|
3
|
+
description: "Use when writing or stabilizing Playwright tests that drive a real browser through multi-step journeys — durable locators, web-first assertions, storageState auth, trace/retries, and flakes that only bite in CI. NOT in-process component tests (that is testing-web), NOT WCAG auditing (that is accessibility), NOT the pre-merge gate (that is verify)."
|
|
4
4
|
tags: [playwright, e2e, browser-testing, flakiness, ci]
|
|
5
5
|
recommends: [testing-web, accessibility, performance, github-actions, debug]
|
|
6
6
|
origin: risco
|
|
@@ -18,17 +18,6 @@ line is **Playwright v1.60.x** (v1.60.0 shipped 2026-05-11). The `_react` / `_vu
|
|
|
18
18
|
and the `:light` Shadow-DOM suffix were **removed in v1.58.0** — at any version you should be pinning
|
|
19
19
|
they are long gone, so do not reach for them.
|
|
20
20
|
|
|
21
|
-
## What good looks like
|
|
22
|
-
|
|
23
|
-
- **User-facing locators.** Tests find elements the way a user does — by role and accessible name —
|
|
24
|
-
not by `div.card > button:nth-child(2)`. The test survives a refactor that the user never sees.
|
|
25
|
-
- **Web-first assertions.** Every assertion auto-retries until the DOM settles. You never read a
|
|
26
|
-
value once and compare it.
|
|
27
|
-
- **Deterministic by design.** No `waitForTimeout`. No test that depends on another test's leftovers.
|
|
28
|
-
Flakiness is a design defect you prevent, not a rerun you tolerate.
|
|
29
|
-
- **Traces on retry.** CI captures a full trace the first time a test retries, so a CI-only failure
|
|
30
|
-
is debuggable from the artifact without a local repro.
|
|
31
|
-
|
|
32
21
|
## Is this even an e2e test?
|
|
33
22
|
|
|
34
23
|
E2e is the most expensive layer. Spend it only on journeys that cross pages or services. Route the rest out.
|
|
@@ -40,6 +29,7 @@ E2e is the most expensive layer. Spend it only on journeys that cross pages or s
|
|
|
40
29
|
| "Is this page accessible?" — WCAG/ARIA as the deliverable | `../accessibility/SKILL.md` | E2e may *call* axe inside a test, but auditing a11y is its own skill. |
|
|
41
30
|
| "Is this page fast?" — LCP/CWV budgets | `../performance/SKILL.md` | Perf budgets are a different signal than journey correctness. |
|
|
42
31
|
| The runner matrix, caching, the pipeline itself | `../github-actions/SKILL.md` | E2e contributes a *job*; owning the pipeline is theirs. |
|
|
32
|
+
| "Is the change done?" — run the gate, collect evidence, then merge | `../verify/SKILL.md` | Running an existing suite as a pre-merge gate is not authoring or stabilizing one. |
|
|
43
33
|
|
|
44
34
|
Rule: if you can prove it without launching a browser, you should. Push logic down to `testing-web`.
|
|
45
35
|
|
package/skills/electron/SKILL.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: electron
|
|
3
|
-
description: "Use when building, hardening, or shipping a cross-platform desktop app
|
|
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`
|
|
17
|
-
|
|
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.**
|
|
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
|
|
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
|
-
##
|
|
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` |
|
|
203
|
-
| `contextIsolation: false` |
|
|
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
|
-
|
|
|
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
|
|