engrm 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (98) hide show
  1. package/README.md +214 -73
  2. package/bin/build.mjs +97 -0
  3. package/bin/engrm.mjs +13 -0
  4. package/dist/cli.js +2712 -0
  5. package/dist/hooks/elicitation-result.js +1786 -0
  6. package/dist/hooks/post-tool-use.js +2357 -0
  7. package/dist/hooks/pre-compact.js +1321 -0
  8. package/dist/hooks/sentinel.js +1168 -0
  9. package/dist/hooks/session-start.js +1473 -0
  10. package/dist/hooks/stop.js +1834 -0
  11. package/dist/server.js +16628 -0
  12. package/package.json +29 -4
  13. package/packs/api-best-practices.json +182 -0
  14. package/packs/nextjs-patterns.json +68 -0
  15. package/packs/node-security.json +68 -0
  16. package/packs/python-django.json +68 -0
  17. package/packs/react-gotchas.json +182 -0
  18. package/packs/typescript-patterns.json +67 -0
  19. package/packs/web-security.json +182 -0
  20. package/.mcp.json +0 -9
  21. package/AUTH-DESIGN.md +0 -436
  22. package/BRIEF.md +0 -197
  23. package/CLAUDE.md +0 -44
  24. package/COMPETITIVE.md +0 -174
  25. package/CONTEXT-OPTIMIZATION.md +0 -305
  26. package/INFRASTRUCTURE.md +0 -252
  27. package/MARKET.md +0 -230
  28. package/PLAN.md +0 -278
  29. package/SENTINEL.md +0 -293
  30. package/SERVER-API-PLAN.md +0 -553
  31. package/SPEC.md +0 -843
  32. package/SWOT.md +0 -148
  33. package/SYNC-ARCHITECTURE.md +0 -294
  34. package/VIBE-CODER-STRATEGY.md +0 -250
  35. package/bun.lock +0 -375
  36. package/hooks/post-tool-use.ts +0 -144
  37. package/hooks/session-start.ts +0 -64
  38. package/hooks/stop.ts +0 -131
  39. package/mem-page.html +0 -1305
  40. package/src/capture/dedup.test.ts +0 -103
  41. package/src/capture/dedup.ts +0 -76
  42. package/src/capture/extractor.test.ts +0 -245
  43. package/src/capture/extractor.ts +0 -330
  44. package/src/capture/quality.test.ts +0 -168
  45. package/src/capture/quality.ts +0 -104
  46. package/src/capture/retrospective.test.ts +0 -115
  47. package/src/capture/retrospective.ts +0 -121
  48. package/src/capture/scanner.test.ts +0 -131
  49. package/src/capture/scanner.ts +0 -100
  50. package/src/capture/scrubber.test.ts +0 -144
  51. package/src/capture/scrubber.ts +0 -181
  52. package/src/cli.ts +0 -517
  53. package/src/config.ts +0 -238
  54. package/src/context/inject.test.ts +0 -940
  55. package/src/context/inject.ts +0 -382
  56. package/src/embeddings/backfill.ts +0 -50
  57. package/src/embeddings/embedder.test.ts +0 -76
  58. package/src/embeddings/embedder.ts +0 -139
  59. package/src/lifecycle/aging.test.ts +0 -103
  60. package/src/lifecycle/aging.ts +0 -36
  61. package/src/lifecycle/compaction.test.ts +0 -264
  62. package/src/lifecycle/compaction.ts +0 -190
  63. package/src/lifecycle/purge.test.ts +0 -100
  64. package/src/lifecycle/purge.ts +0 -37
  65. package/src/lifecycle/scheduler.test.ts +0 -120
  66. package/src/lifecycle/scheduler.ts +0 -101
  67. package/src/provisioning/browser-auth.ts +0 -172
  68. package/src/provisioning/provision.test.ts +0 -198
  69. package/src/provisioning/provision.ts +0 -94
  70. package/src/register.test.ts +0 -167
  71. package/src/register.ts +0 -178
  72. package/src/server.ts +0 -436
  73. package/src/storage/migrations.test.ts +0 -244
  74. package/src/storage/migrations.ts +0 -261
  75. package/src/storage/outbox.test.ts +0 -229
  76. package/src/storage/outbox.ts +0 -131
  77. package/src/storage/projects.test.ts +0 -137
  78. package/src/storage/projects.ts +0 -184
  79. package/src/storage/sqlite.test.ts +0 -798
  80. package/src/storage/sqlite.ts +0 -934
  81. package/src/storage/vec.test.ts +0 -198
  82. package/src/sync/auth.test.ts +0 -76
  83. package/src/sync/auth.ts +0 -68
  84. package/src/sync/client.ts +0 -183
  85. package/src/sync/engine.test.ts +0 -94
  86. package/src/sync/engine.ts +0 -127
  87. package/src/sync/pull.test.ts +0 -279
  88. package/src/sync/pull.ts +0 -170
  89. package/src/sync/push.test.ts +0 -117
  90. package/src/sync/push.ts +0 -230
  91. package/src/tools/get.ts +0 -34
  92. package/src/tools/pin.ts +0 -47
  93. package/src/tools/save.test.ts +0 -301
  94. package/src/tools/save.ts +0 -231
  95. package/src/tools/search.test.ts +0 -69
  96. package/src/tools/search.ts +0 -181
  97. package/src/tools/timeline.ts +0 -64
  98. package/tsconfig.json +0 -22
@@ -0,0 +1,182 @@
1
+ {
2
+ "name": "react-gotchas",
3
+ "description": "Common React pitfalls, performance issues, and best practices",
4
+ "version": "1.0.0",
5
+ "observations": [
6
+ {
7
+ "type": "pattern",
8
+ "title": "Use key prop correctly — index keys cause bugs with reordering",
9
+ "narrative": "React uses keys to track list identity. Using array index as key causes incorrect component reuse when items are reordered, deleted, or inserted.",
10
+ "facts": ["Use stable unique IDs as keys (database IDs, UUIDs)", "Index keys are only safe for static lists that never change", "Missing keys cause React to warn and use index implicitly"],
11
+ "concepts": ["react-keys", "list-rendering", "reconciliation"]
12
+ },
13
+ {
14
+ "type": "bugfix",
15
+ "title": "useEffect cleanup: return a cleanup function to avoid memory leaks",
16
+ "narrative": "Effects that set up subscriptions, timers, or event listeners must return a cleanup function. Without it, listeners accumulate on re-renders.",
17
+ "facts": ["Return a function from useEffect for cleanup", "Cleanup runs before re-running the effect and on unmount", "Common sources: setInterval, addEventListener, WebSocket connections"],
18
+ "concepts": ["useEffect", "cleanup", "memory-leak", "subscriptions"]
19
+ },
20
+ {
21
+ "type": "pattern",
22
+ "title": "Avoid creating objects/arrays in render — causes unnecessary re-renders",
23
+ "narrative": "New object/array literals in JSX (style={{}}, options={[]}) create new references every render, defeating React.memo and causing child re-renders.",
24
+ "facts": ["Move constant objects outside the component", "Use useMemo for computed objects", "Use useCallback for function props", "React.memo compares props by reference"],
25
+ "concepts": ["referential-equality", "useMemo", "useCallback", "performance"]
26
+ },
27
+ {
28
+ "type": "bugfix",
29
+ "title": "State updates are batched — don't read state immediately after setState",
30
+ "narrative": "React batches state updates for performance. Reading state right after setState gives the old value. Use the callback form or useEffect to react to changes.",
31
+ "facts": ["setState is asynchronous in event handlers", "Use functional updates: setState(prev => prev + 1)", "useEffect with the state as dependency reacts to changes", "React 18 batches all updates, including in promises and timeouts"],
32
+ "concepts": ["state-batching", "setState", "async-state", "react-18"]
33
+ },
34
+ {
35
+ "type": "pattern",
36
+ "title": "Lift state up only as high as needed — avoid prop drilling",
37
+ "narrative": "State should live in the lowest common ancestor that needs it. For deeply nested access, use Context or a state management library instead of passing through many layers.",
38
+ "facts": ["Prop drilling through 2-3 levels is fine", "Beyond that, consider Context or state libraries", "Context re-renders all consumers — keep contexts focused", "Composition (passing children) often eliminates prop drilling"],
39
+ "concepts": ["state-management", "prop-drilling", "context", "component-composition"]
40
+ },
41
+ {
42
+ "type": "discovery",
43
+ "title": "React.StrictMode double-invokes effects to catch bugs",
44
+ "narrative": "In development, StrictMode intentionally runs effects twice to help find cleanup bugs. This is normal and doesn't happen in production.",
45
+ "facts": ["Effects run twice in dev with StrictMode", "This catches missing cleanup functions", "Does not happen in production builds", "Don't remove StrictMode to 'fix' double renders"],
46
+ "concepts": ["strict-mode", "development", "debugging"]
47
+ },
48
+ {
49
+ "type": "pattern",
50
+ "title": "Use React.lazy and Suspense for code splitting",
51
+ "narrative": "Large bundles slow initial load. Split code at route boundaries using React.lazy() with Suspense fallback. This loads components on demand.",
52
+ "facts": ["React.lazy(() => import('./Component')) for dynamic import", "Wrap with <Suspense fallback={<Loading/>}>", "Split at route level first for biggest impact", "Avoid lazy for above-the-fold content"],
53
+ "concepts": ["code-splitting", "lazy-loading", "suspense", "performance"]
54
+ },
55
+ {
56
+ "type": "bugfix",
57
+ "title": "Closures in useEffect capture stale state values",
58
+ "narrative": "Effects close over the state from the render they were created in. Without proper dependencies, they reference stale values.",
59
+ "facts": ["Always include used variables in the dependency array", "Use the eslint exhaustive-deps rule", "Functional updates (setCount(c => c + 1)) avoid stale closure", "Refs (useRef) provide a mutable escape hatch"],
60
+ "concepts": ["stale-closures", "useEffect-dependencies", "hooks-rules"]
61
+ },
62
+ {
63
+ "type": "pattern",
64
+ "title": "Error boundaries catch render errors — use them at route level",
65
+ "narrative": "Unhandled errors in render crash the entire app. Error boundaries (class components with componentDidCatch) prevent this by catching errors in their subtree.",
66
+ "facts": ["Error boundaries must be class components", "Catch errors in render, lifecycle, and constructors", "Don't catch errors in event handlers — use try/catch", "Place boundaries at route level and around risky components"],
67
+ "concepts": ["error-boundary", "error-handling", "resilience"]
68
+ },
69
+ {
70
+ "type": "pattern",
71
+ "title": "Avoid derived state — compute during render instead",
72
+ "narrative": "Storing values derived from props/state in separate state causes sync bugs. Compute derived values during render or use useMemo for expensive computations.",
73
+ "facts": ["Derived state = state computed from other state/props", "Compute it inline or with useMemo", "getDerivedStateFromProps is almost always wrong", "Filtering/sorting lists is a common case — useMemo, don't store"],
74
+ "concepts": ["derived-state", "useMemo", "anti-pattern"]
75
+ },
76
+ {
77
+ "type": "bugfix",
78
+ "title": "Don't mutate state directly — always create new references",
79
+ "narrative": "React detects changes by reference comparison. Mutating objects/arrays in state (push, splice, obj.key = val) won't trigger re-renders.",
80
+ "facts": ["Use spread: setState({...state, key: val})", "For arrays: [...arr, newItem], arr.filter(), arr.map()", "Use structuredClone() for deep copies", "Libraries like Immer make immutable updates easier"],
81
+ "concepts": ["immutability", "state-updates", "reference-equality"]
82
+ },
83
+ {
84
+ "type": "pattern",
85
+ "title": "useId for unique IDs — don't use Math.random in render",
86
+ "narrative": "Components needing unique IDs (for label-input associations, ARIA) should use React.useId() which is stable across server/client rendering.",
87
+ "facts": ["useId generates deterministic IDs safe for SSR", "Math.random() causes hydration mismatches", "useId works with multiple IDs: id + '-name', id + '-email'"],
88
+ "concepts": ["useId", "accessibility", "ssr-hydration"]
89
+ },
90
+ {
91
+ "type": "pattern",
92
+ "title": "Debounce search inputs — don't fire API calls on every keystroke",
93
+ "narrative": "Text inputs connected to search should debounce the API call. Use a custom hook or useDeferredValue to avoid flooding the server.",
94
+ "facts": ["Debounce with 200-300ms delay for search", "useDeferredValue is React 18's built-in approach", "AbortController cancels in-flight requests", "Show stale results while loading new ones"],
95
+ "concepts": ["debounce", "search", "useDeferredValue", "performance"]
96
+ },
97
+ {
98
+ "type": "discovery",
99
+ "title": "React DevTools Profiler identifies unnecessary re-renders",
100
+ "narrative": "The Profiler in React DevTools shows which components re-render and why. Use it to find performance bottlenecks instead of premature optimization.",
101
+ "facts": ["Enable 'Highlight updates' to see re-renders visually", "Profiler shows render time and commit info", "Check 'Why did this render?' with the option enabled", "Optimize only measured bottlenecks"],
102
+ "concepts": ["react-devtools", "profiling", "performance-debugging"]
103
+ },
104
+ {
105
+ "type": "pattern",
106
+ "title": "Controlled vs uncontrolled inputs: pick one and be consistent",
107
+ "narrative": "Mixing controlled (value + onChange) and uncontrolled (defaultValue + ref) patterns on the same input causes React warnings and bugs.",
108
+ "facts": ["Controlled: value={state} + onChange handler", "Uncontrolled: defaultValue + useRef to read on submit", "Switching from uncontrolled to controlled is a warning", "Forms with many fields: consider react-hook-form (uncontrolled)"],
109
+ "concepts": ["controlled-components", "forms", "useRef"]
110
+ },
111
+ {
112
+ "type": "pattern",
113
+ "title": "Use React.memo strategically — not on every component",
114
+ "narrative": "React.memo prevents re-renders when props haven't changed. But it has a cost (shallow comparison) and is only worthwhile for components that re-render often with same props.",
115
+ "facts": ["Measure before memoizing", "Useless if parent always passes new object/function props", "Pair with useCallback/useMemo for prop stability", "List items and expensive renders benefit most"],
116
+ "concepts": ["react-memo", "memoization", "performance-optimization"]
117
+ },
118
+ {
119
+ "type": "bugfix",
120
+ "title": "useEffect with empty deps runs once — watch for missing dependencies",
121
+ "narrative": "useEffect(() => {...}, []) runs only on mount. If the effect uses props or state, those values will be stale forever. Add them to the dependency array.",
122
+ "facts": ["Empty array = run once on mount", "Missing deps = stale values (subtle bug)", "ESLint exhaustive-deps rule catches this", "If you truly want run-once, ensure no external deps are used"],
123
+ "concepts": ["useEffect", "dependency-array", "stale-closures"]
124
+ },
125
+ {
126
+ "type": "pattern",
127
+ "title": "Prefer composition over configuration for flexible components",
128
+ "narrative": "Instead of adding more and more props to configure a component, compose smaller components. Slots (children, render props) are more flexible than config objects.",
129
+ "facts": ["Composition: <Card><CardHeader/><CardBody/></Card>", "Configuration: <Card title='...' body='...' footer='...'/>", "Composition scales better and avoids prop explosion", "React children pattern is the simplest form of composition"],
130
+ "concepts": ["composition", "component-design", "render-props", "children"]
131
+ },
132
+ {
133
+ "type": "bugfix",
134
+ "title": "Conditional hooks violate Rules of Hooks — always call in same order",
135
+ "narrative": "Hooks must be called in the same order every render. Putting hooks inside if/else, loops, or after early returns causes React to lose track of state.",
136
+ "facts": ["Hooks rely on call order for identity", "Move conditions inside the hook instead", "eslint-plugin-react-hooks enforces this", "Custom hooks must also follow this rule"],
137
+ "concepts": ["rules-of-hooks", "conditional-hooks", "hook-order"]
138
+ },
139
+ {
140
+ "type": "pattern",
141
+ "title": "Use AbortController to cancel fetch requests on unmount",
142
+ "narrative": "Fetch requests that complete after a component unmounts cause 'setState on unmounted component' warnings. Use AbortController in the cleanup function.",
143
+ "facts": ["Create AbortController in effect, pass signal to fetch", "Abort in cleanup: return () => controller.abort()", "Aborted fetch throws AbortError — catch and ignore it", "Works with axios via cancelToken too"],
144
+ "concepts": ["fetch", "abort-controller", "cleanup", "useEffect"]
145
+ },
146
+ {
147
+ "type": "pattern",
148
+ "title": "Server Components (RSC) run on the server — no state, no effects",
149
+ "narrative": "React Server Components execute at build/request time on the server. They can't use useState, useEffect, or browser APIs. Use 'use client' directive for interactive components.",
150
+ "facts": ["Server Components are the default in Next.js App Router", "Add 'use client' at top of file for client components", "Server Components can async/await directly", "Pass server data as props to client components"],
151
+ "concepts": ["server-components", "rsc", "nextjs", "use-client"]
152
+ },
153
+ {
154
+ "type": "pattern",
155
+ "title": "Virtualize long lists — don't render 1000+ items in DOM",
156
+ "narrative": "Rendering thousands of DOM nodes kills performance. Use react-window or react-virtuoso to only render visible items.",
157
+ "facts": ["Virtual lists render only visible rows", "react-window is lightweight (6kb)", "react-virtuoso handles variable heights", "Measure first — small lists (<100 items) don't need virtualization"],
158
+ "concepts": ["virtualization", "performance", "react-window", "long-lists"]
159
+ },
160
+ {
161
+ "type": "pattern",
162
+ "title": "Use useReducer for complex state logic with multiple sub-values",
163
+ "narrative": "When state has multiple related values or complex update logic, useReducer provides clearer state transitions than multiple useState calls.",
164
+ "facts": ["useReducer is better for related state values", "Actions describe what happened, reducer decides how state changes", "Easier to test — reducer is a pure function", "Combine with useContext for app-level state"],
165
+ "concepts": ["useReducer", "state-management", "complex-state"]
166
+ },
167
+ {
168
+ "type": "discovery",
169
+ "title": "React 19: use() hook for promises and context",
170
+ "narrative": "React 19 introduces the use() hook which can read promises (with Suspense) and context values. It can be called conditionally, unlike other hooks.",
171
+ "facts": ["use(promise) suspends until promise resolves", "use(context) replaces useContext(context)", "Can be called inside if/else (exception to hook rules)", "Works with server components and client components"],
172
+ "concepts": ["react-19", "use-hook", "suspense", "context"]
173
+ },
174
+ {
175
+ "type": "pattern",
176
+ "title": "Handle loading, error, and empty states explicitly",
177
+ "narrative": "Every data-fetching component needs three states: loading (skeleton/spinner), error (retry/message), and empty (helpful message/CTA). Don't just handle the happy path.",
178
+ "facts": ["Skeletons are better UX than spinners for content", "Show retry button on error", "Empty state should guide user to action", "Consider using Suspense + ErrorBoundary for this pattern"],
179
+ "concepts": ["loading-states", "error-states", "empty-states", "ux"]
180
+ }
181
+ ]
182
+ }
@@ -0,0 +1,67 @@
1
+ {
2
+ "name": "typescript-patterns",
3
+ "description": "Common TypeScript patterns, gotchas, and best practices",
4
+ "stacks": ["typescript"],
5
+ "observations": [
6
+ {
7
+ "type": "pattern",
8
+ "title": "Use 'satisfies' for type-safe config objects",
9
+ "narrative": "The 'satisfies' operator validates a value matches a type without widening it. Prefer `const config = { ... } satisfies Config` over `const config: Config = { ... }` to preserve literal types.",
10
+ "concepts": ["typescript", "satisfies", "type-safety"]
11
+ },
12
+ {
13
+ "type": "pattern",
14
+ "title": "Discriminated unions over optional properties",
15
+ "narrative": "Use a shared literal 'kind' or 'type' field to discriminate union variants. This enables exhaustive switch/case checking and eliminates impossible states that optional properties allow.",
16
+ "concepts": ["typescript", "discriminated-union", "type-safety"]
17
+ },
18
+ {
19
+ "type": "pattern",
20
+ "title": "Use 'as const' for literal type inference",
21
+ "narrative": "Apply 'as const' to object/array literals when you need TypeScript to infer exact string/number literal types instead of widened string/number types.",
22
+ "concepts": ["typescript", "const-assertion", "literal-types"]
23
+ },
24
+ {
25
+ "type": "discovery",
26
+ "title": "Template literal types for string patterns",
27
+ "narrative": "TypeScript supports template literal types like `type Route = \\`/api/${string}\\`` to constrain string shapes at the type level. Useful for API paths, CSS values, and event names.",
28
+ "concepts": ["typescript", "template-literal-types"]
29
+ },
30
+ {
31
+ "type": "bugfix",
32
+ "title": "Avoid 'any' leaking through generic defaults",
33
+ "narrative": "Generic functions with `<T = any>` silently disable type checking for callers who don't provide the type parameter. Use `<T = unknown>` or `<T extends Base>` instead.",
34
+ "concepts": ["typescript", "generics", "any-leak"]
35
+ },
36
+ {
37
+ "type": "pattern",
38
+ "title": "Branded types for domain identifiers",
39
+ "narrative": "Prevent mixing up IDs like userId and orderId by creating branded types: `type UserId = string & { __brand: 'UserId' }`. Create helper functions to construct them.",
40
+ "concepts": ["typescript", "branded-types", "domain-modeling"]
41
+ },
42
+ {
43
+ "type": "pattern",
44
+ "title": "Use 'unknown' over 'any' for safe catch blocks",
45
+ "narrative": "In catch blocks, use `catch (err: unknown)` and narrow with `err instanceof Error` before accessing .message. This prevents runtime crashes from non-Error throws.",
46
+ "concepts": ["typescript", "error-handling", "unknown"]
47
+ },
48
+ {
49
+ "type": "pattern",
50
+ "title": "Record<string, T> vs Map<string, T>",
51
+ "narrative": "Use Record for static key sets known at compile time. Use Map when keys are dynamic, when you need size/iteration order guarantees, or when non-string keys are needed.",
52
+ "concepts": ["typescript", "record", "map", "data-structures"]
53
+ },
54
+ {
55
+ "type": "discovery",
56
+ "title": "Infer return types from implementation",
57
+ "narrative": "For internal functions, let TypeScript infer the return type from the implementation. Only add explicit return types at module boundaries (exports) or when the inference is too wide.",
58
+ "concepts": ["typescript", "return-types", "type-inference"]
59
+ },
60
+ {
61
+ "type": "pattern",
62
+ "title": "Zod schemas as single source of truth",
63
+ "narrative": "Define Zod schemas first, then derive TypeScript types with z.infer<typeof schema>. This gives you both runtime validation and compile-time types from a single definition.",
64
+ "concepts": ["typescript", "zod", "validation", "schema"]
65
+ }
66
+ ]
67
+ }
@@ -0,0 +1,182 @@
1
+ {
2
+ "name": "web-security",
3
+ "description": "Common web security patterns, vulnerabilities, and best practices",
4
+ "version": "1.0.0",
5
+ "observations": [
6
+ {
7
+ "type": "pattern",
8
+ "title": "Always validate and sanitize user input on the server side",
9
+ "narrative": "Client-side validation is a UX feature, not a security measure. All user input must be validated and sanitized server-side before use in queries, file operations, or rendering.",
10
+ "facts": ["Client-side validation can be bypassed trivially", "Server-side validation is the real security boundary", "Use allowlists over denylists when possible"],
11
+ "concepts": ["input-validation", "xss-prevention", "server-side-security"]
12
+ },
13
+ {
14
+ "type": "pattern",
15
+ "title": "Use parameterized queries to prevent SQL injection",
16
+ "narrative": "Never concatenate user input into SQL strings. Use parameterized queries or prepared statements provided by your database library.",
17
+ "facts": ["SQL injection is consistently in OWASP Top 10", "Parameterized queries separate code from data", "ORMs generally handle this but raw queries need explicit parameterization"],
18
+ "concepts": ["sql-injection", "parameterized-queries", "prepared-statements"]
19
+ },
20
+ {
21
+ "type": "pattern",
22
+ "title": "Set Content-Security-Policy headers to mitigate XSS",
23
+ "narrative": "CSP headers instruct browsers to only execute scripts from trusted sources, dramatically reducing the attack surface for cross-site scripting.",
24
+ "facts": ["Start with a restrictive policy and loosen as needed", "Use nonces or hashes for inline scripts", "Report-only mode helps during rollout"],
25
+ "concepts": ["csp", "xss", "http-headers", "content-security-policy"]
26
+ },
27
+ {
28
+ "type": "pattern",
29
+ "title": "Store passwords using bcrypt, scrypt, or Argon2",
30
+ "narrative": "Never store passwords in plaintext or with fast hashing algorithms like MD5/SHA. Use purpose-built password hashing with salt and work factor.",
31
+ "facts": ["Argon2id is the current recommendation", "bcrypt with cost factor 12+ is still acceptable", "Always use unique salts per password", "Never use MD5 or SHA for passwords"],
32
+ "concepts": ["password-hashing", "bcrypt", "argon2", "authentication"]
33
+ },
34
+ {
35
+ "type": "pattern",
36
+ "title": "Implement CSRF protection for state-changing requests",
37
+ "narrative": "Cross-Site Request Forgery tricks authenticated users into making unwanted requests. Use anti-CSRF tokens, SameSite cookies, and verify Origin headers.",
38
+ "facts": ["SameSite=Lax cookies prevent most CSRF attacks", "CSRF tokens must be unique per session", "GET requests should never modify state"],
39
+ "concepts": ["csrf", "cookies", "session-security"]
40
+ },
41
+ {
42
+ "type": "discovery",
43
+ "title": "HttpOnly and Secure flags on session cookies prevent theft",
44
+ "narrative": "HttpOnly prevents JavaScript access to cookies (mitigating XSS cookie theft). Secure ensures cookies are only sent over HTTPS.",
45
+ "facts": ["HttpOnly blocks document.cookie access", "Secure flag requires HTTPS transport", "SameSite=Strict prevents cross-origin sending"],
46
+ "concepts": ["cookies", "session-management", "http-only", "secure-flag"]
47
+ },
48
+ {
49
+ "type": "pattern",
50
+ "title": "Rate limit authentication endpoints to prevent brute force",
51
+ "narrative": "Login, registration, and password reset endpoints must have rate limiting to prevent credential stuffing and brute force attacks.",
52
+ "facts": ["Use progressive delays (1s, 2s, 4s, 8s) or hard lockout after N attempts", "Rate limit by IP and by account independently", "Consider CAPTCHA after 3-5 failed attempts"],
53
+ "concepts": ["rate-limiting", "brute-force", "authentication", "account-security"]
54
+ },
55
+ {
56
+ "type": "pattern",
57
+ "title": "Validate file uploads: type, size, and content",
58
+ "narrative": "File uploads are a common attack vector. Validate MIME type, file extension, file size, and ideally scan content. Never trust the client-provided filename.",
59
+ "facts": ["Check magic bytes, not just extension", "Store uploads outside webroot", "Generate new filenames server-side", "Set maximum file size limits"],
60
+ "concepts": ["file-upload", "content-type", "path-traversal"]
61
+ },
62
+ {
63
+ "type": "pattern",
64
+ "title": "Use HTTPS everywhere — no exceptions for internal services",
65
+ "narrative": "TLS encrypts data in transit. Internal services communicate over networks that can be compromised. Use TLS for all service-to-service communication.",
66
+ "facts": ["Let's Encrypt provides free certificates", "HSTS header prevents downgrade attacks", "Internal services should also use TLS"],
67
+ "concepts": ["https", "tls", "hsts", "encryption-in-transit"]
68
+ },
69
+ {
70
+ "type": "discovery",
71
+ "title": "Environment variables for secrets, never in code or config files",
72
+ "narrative": "API keys, database passwords, and tokens must come from environment variables or a secrets manager. Never commit them to version control.",
73
+ "facts": ["Use .env files locally (gitignored)", "Use secrets manager in production (AWS SM, Vault, etc.)", "Rotate secrets regularly", "Audit git history for accidentally committed secrets"],
74
+ "concepts": ["secrets-management", "environment-variables", "api-keys"]
75
+ },
76
+ {
77
+ "type": "pattern",
78
+ "title": "Implement proper CORS configuration",
79
+ "narrative": "Cross-Origin Resource Sharing must be configured to allow only trusted origins. Wildcards in CORS headers are dangerous for authenticated APIs.",
80
+ "facts": ["Never use Access-Control-Allow-Origin: * with credentials", "Whitelist specific origins", "Preflight requests (OPTIONS) must be handled correctly"],
81
+ "concepts": ["cors", "cross-origin", "api-security"]
82
+ },
83
+ {
84
+ "type": "pattern",
85
+ "title": "Escape output based on context: HTML, JS, URL, CSS",
86
+ "narrative": "Output encoding must match the context where data is rendered. HTML escaping doesn't protect against JavaScript injection in script tags.",
87
+ "facts": ["HTML context: escape < > & \" '", "JavaScript context: use JSON.stringify or \\xHH encoding", "URL context: use encodeURIComponent", "CSS context: use \\HH encoding"],
88
+ "concepts": ["output-encoding", "xss-prevention", "context-aware-escaping"]
89
+ },
90
+ {
91
+ "type": "pattern",
92
+ "title": "Use helmet.js or equivalent for security headers in Node.js",
93
+ "narrative": "Security headers like X-Frame-Options, X-Content-Type-Options, and Strict-Transport-Security should be set on all responses. Helmet.js sets sensible defaults.",
94
+ "facts": ["X-Frame-Options: DENY prevents clickjacking", "X-Content-Type-Options: nosniff prevents MIME sniffing", "Referrer-Policy controls information leakage"],
95
+ "concepts": ["security-headers", "helmet", "node-security"]
96
+ },
97
+ {
98
+ "type": "pattern",
99
+ "title": "JWT tokens: short expiry, secure storage, validate all claims",
100
+ "narrative": "JWTs are not session tokens. Use short expiry (15min), refresh tokens for renewal, validate iss/aud/exp claims, and never store in localStorage for sensitive apps.",
101
+ "facts": ["Short-lived access tokens (5-15 min)", "Refresh tokens in httpOnly cookies", "Validate algorithm to prevent 'none' attack", "Check token revocation for sensitive operations"],
102
+ "concepts": ["jwt", "authentication", "token-security", "session-management"]
103
+ },
104
+ {
105
+ "type": "discovery",
106
+ "title": "Dependency vulnerabilities: audit regularly with automated tools",
107
+ "narrative": "Third-party packages introduce supply chain risk. Run npm audit, Snyk, or Dependabot regularly. Pin versions and review changelogs before upgrading.",
108
+ "facts": ["Run npm audit or yarn audit in CI", "Use lock files for reproducible builds", "Review dependency tree for unnecessary transitive deps", "Consider using Socket.dev for supply chain analysis"],
109
+ "concepts": ["dependency-security", "npm-audit", "supply-chain", "vulnerability-scanning"]
110
+ },
111
+ {
112
+ "type": "pattern",
113
+ "title": "Implement proper error handling — never leak stack traces",
114
+ "narrative": "Production error responses should be generic. Stack traces, SQL errors, and internal paths help attackers understand your system.",
115
+ "facts": ["Return generic error messages to clients", "Log detailed errors server-side", "Different error handling for dev vs prod", "Never expose database error details"],
116
+ "concepts": ["error-handling", "information-disclosure", "production-security"]
117
+ },
118
+ {
119
+ "type": "pattern",
120
+ "title": "Authorization checks on every endpoint, not just the frontend",
121
+ "narrative": "Broken access control is OWASP #1. Every API endpoint must verify the user has permission for the requested resource. Frontend route guards are not security.",
122
+ "facts": ["Check authorization server-side on every request", "Use middleware for consistent enforcement", "IDOR: always verify resource ownership", "Principle of least privilege"],
123
+ "concepts": ["authorization", "access-control", "idor", "broken-access-control"]
124
+ },
125
+ {
126
+ "type": "pattern",
127
+ "title": "Use Subresource Integrity (SRI) for CDN-loaded scripts",
128
+ "narrative": "SRI hashes ensure that scripts loaded from CDNs haven't been tampered with. If the hash doesn't match, the browser refuses to execute the script.",
129
+ "facts": ["Add integrity attribute to script and link tags", "Generate hashes with shasum or online tools", "Combine with crossorigin='anonymous'"],
130
+ "concepts": ["sri", "cdn-security", "supply-chain", "integrity"]
131
+ },
132
+ {
133
+ "type": "bugfix",
134
+ "title": "Path traversal: normalize and validate file paths",
135
+ "narrative": "Attackers use ../ sequences to access files outside intended directories. Always normalize paths and verify they resolve within the expected base directory.",
136
+ "facts": ["Use path.resolve() then check startsWith(baseDir)", "Reject paths containing .. after normalization", "URL-encoded ../ (%2e%2e%2f) can bypass naive checks"],
137
+ "concepts": ["path-traversal", "directory-traversal", "file-security"]
138
+ },
139
+ {
140
+ "type": "pattern",
141
+ "title": "Implement request signing for API-to-API communication",
142
+ "narrative": "For service-to-service calls, use HMAC request signing or mutual TLS instead of static API keys. This prevents replay attacks and ensures message integrity.",
143
+ "facts": ["Include timestamp in signed payload to prevent replay", "HMAC-SHA256 is widely supported", "Mutual TLS provides both authentication and encryption"],
144
+ "concepts": ["request-signing", "hmac", "mutual-tls", "api-security"]
145
+ },
146
+ {
147
+ "type": "discovery",
148
+ "title": "Content-Type validation prevents polyglot file attacks",
149
+ "narrative": "Uploaded files can be valid in multiple formats simultaneously (polyglot). Validate Content-Type matches actual file content using magic bytes.",
150
+ "facts": ["A file can be both valid JPEG and valid HTML", "Check first few bytes (magic number) against claimed type", "Re-encode images server-side to strip embedded payloads"],
151
+ "concepts": ["polyglot-files", "content-type", "file-upload-security"]
152
+ },
153
+ {
154
+ "type": "pattern",
155
+ "title": "Log security events with structured data for incident response",
156
+ "narrative": "Authentication failures, authorization denials, input validation failures, and admin actions should be logged with structured data for security monitoring.",
157
+ "facts": ["Include: timestamp, user_id, IP, action, resource, outcome", "Never log passwords, tokens, or PII", "Use structured logging (JSON) for machine parsing", "Forward to SIEM for alerting"],
158
+ "concepts": ["security-logging", "audit-trail", "incident-response"]
159
+ },
160
+ {
161
+ "type": "pattern",
162
+ "title": "Implement account lockout with exponential backoff",
163
+ "narrative": "After repeated failed login attempts, temporarily lock the account. Use exponential backoff to make brute force infeasible while limiting impact on legitimate users.",
164
+ "facts": ["Lock after 5-10 failed attempts", "Exponential backoff: 1min, 5min, 15min, 1hr", "Notify user via email on lockout", "Track attempts per account AND per IP"],
165
+ "concepts": ["account-lockout", "brute-force-protection", "authentication"]
166
+ },
167
+ {
168
+ "type": "discovery",
169
+ "title": "Server-Side Request Forgery (SSRF) via user-supplied URLs",
170
+ "narrative": "When your server fetches URLs provided by users (webhooks, image URLs, etc.), attackers can target internal services. Validate and restrict allowed destinations.",
171
+ "facts": ["Block requests to private IP ranges (10.x, 172.16-31.x, 192.168.x)", "Use allowlists for permitted domains when possible", "DNS rebinding can bypass IP checks — resolve DNS before connecting", "Cloud metadata endpoints (169.254.169.254) are high-value targets"],
172
+ "concepts": ["ssrf", "url-validation", "internal-network", "cloud-security"]
173
+ },
174
+ {
175
+ "type": "pattern",
176
+ "title": "Use nonces for inline scripts with CSP",
177
+ "narrative": "If you must use inline scripts, generate a random nonce per request and include it in both the CSP header and the script tag. Never use unsafe-inline.",
178
+ "facts": ["Nonce must be cryptographically random per request", "Add nonce to CSP: script-src 'nonce-abc123'", "Add to script tag: <script nonce='abc123'>", "unsafe-inline defeats the purpose of CSP"],
179
+ "concepts": ["csp-nonce", "inline-scripts", "xss-prevention"]
180
+ }
181
+ ]
182
+ }
package/.mcp.json DELETED
@@ -1,9 +0,0 @@
1
- {
2
- "mcpServers": {
3
- "engrm": {
4
- "type": "stdio",
5
- "command": "/Users/david/.bun/bin/bun",
6
- "args": ["run", "src/server.ts"]
7
- }
8
- }
9
- }