Security and performance fixes addressing a comprehensive review: - Server-issued HMAC-signed session cookies; client-supplied session_id ignored. Prevents session hijacking via body substitution. - Sliding-window rate limiter per IP and per session. - SessionStore with LRU eviction, idle TTL, per-session threading locks, and a hard turn cap. Bounds memory and serializes concurrent turns for the same session so FastAPI's threadpool cannot corrupt history. - Tool-use loop capped at settings.max_tool_use_iterations; Anthropic client gets an explicit timeout. No more infinite-loop credit burn. - Every tool argument is regex-validated, length-capped, and control-character-stripped. asserts replaced with ValueError so -O cannot silently disable the checks. - PII-safe warning logs: session IDs and reply bodies are hashed, never logged in clear. - hmac.compare_digest for email comparison (constant-time). - Strict Content-Security-Policy plus X-Content-Type-Options, X-Frame-Options, Referrer-Policy, Permissions-Policy via middleware. - Explicit handlers for anthropic.RateLimitError, APIConnectionError, APIStatusError, ValueError; static dir resolved from __file__. - Prompt cache breakpoints on the last tool schema and the last message so per-turn input cost scales linearly, not quadratically. - TypedDict handler argument shapes; direct block.name/block.id access. - functools.lru_cache on _get_client. - Anchored word-boundary regexes for out-of-scope detection to kill false positives on phrases like "I'd recommend contacting...". Literate program: - Bookly.lit.md is now the single source of truth for the five core Python files. Tangles byte-for-byte; verified via tangle.ts --verify. - Prose walkthrough, three mermaid diagrams, narrative per module. - Woven to static/architecture.html with the app's palette (background #f5f3ee) via scripts/architecture-header.html. - New GET /architecture route serves the HTML with a relaxed CSP that allows pandoc's inline styles. Available at bookly.codyborders.com/architecture. - scripts/rebuild_architecture_html.sh regenerates the HTML after edits. - code_reviews/2026-04-15-1433-code-review.md captures the review that drove these changes. All 37 tests pass.
105 lines
2.6 KiB
HTML
105 lines
2.6 KiB
HTML
<style>
|
|
:root {
|
|
--bg: #f5f3ee;
|
|
--panel: #ffffff;
|
|
--ink: #1a1a1a;
|
|
--ink-muted: #6b6b6b;
|
|
--accent: #2e5b8a;
|
|
--border: #e2ddd2;
|
|
--code-bg: #f0ede4;
|
|
}
|
|
html, body {
|
|
background: var(--bg);
|
|
color: var(--ink);
|
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
|
|
"Helvetica Neue", Arial, sans-serif;
|
|
font-size: 16px;
|
|
line-height: 1.6;
|
|
margin: 0;
|
|
}
|
|
body {
|
|
max-width: 820px;
|
|
margin: 0 auto;
|
|
padding: 48px 32px 96px;
|
|
}
|
|
h1, h2, h3, h4 {
|
|
color: var(--ink);
|
|
letter-spacing: -0.01em;
|
|
line-height: 1.25;
|
|
margin-top: 2.2em;
|
|
margin-bottom: 0.6em;
|
|
}
|
|
h1.title {
|
|
font-size: 44px;
|
|
font-weight: 700;
|
|
margin-top: 0;
|
|
margin-bottom: 0.2em;
|
|
letter-spacing: -0.02em;
|
|
}
|
|
h1 { font-size: 28px; font-weight: 700; border-bottom: 1px solid var(--border); padding-bottom: 0.3em; }
|
|
h2 { font-size: 22px; font-weight: 600; }
|
|
h3 { font-size: 18px; font-weight: 600; color: var(--ink-muted); }
|
|
p { margin: 0 0 1.1em; }
|
|
a { color: var(--accent); text-decoration: none; }
|
|
a:hover { text-decoration: underline; }
|
|
code {
|
|
font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace;
|
|
font-size: 0.88em;
|
|
background: var(--code-bg);
|
|
padding: 0.1em 0.35em;
|
|
border-radius: 4px;
|
|
border: 1px solid var(--border);
|
|
}
|
|
pre {
|
|
background: var(--panel);
|
|
border: 1px solid var(--border);
|
|
border-radius: 8px;
|
|
padding: 16px 20px;
|
|
overflow-x: auto;
|
|
font-size: 13.5px;
|
|
line-height: 1.55;
|
|
}
|
|
pre code {
|
|
background: transparent;
|
|
border: none;
|
|
padding: 0;
|
|
font-size: inherit;
|
|
}
|
|
.sourceCode { background: transparent; }
|
|
#TOC {
|
|
background: var(--panel);
|
|
border: 1px solid var(--border);
|
|
border-radius: 8px;
|
|
padding: 20px 28px;
|
|
margin: 28px 0 40px;
|
|
}
|
|
#TOC ul { list-style: none; padding-left: 1.1em; margin: 0.3em 0; }
|
|
#TOC > ul { padding-left: 0; }
|
|
#TOC li { margin: 0.25em 0; }
|
|
#TOC a { color: var(--ink); }
|
|
.mermaid, .diagram, figure {
|
|
background: var(--panel);
|
|
border: 1px solid var(--border);
|
|
border-radius: 8px;
|
|
padding: 20px;
|
|
margin: 24px 0;
|
|
text-align: center;
|
|
}
|
|
figure img, figure svg, p img { max-width: 100%; height: auto; }
|
|
blockquote {
|
|
border-left: 3px solid var(--accent);
|
|
margin: 1.2em 0;
|
|
padding: 0.2em 1em;
|
|
color: var(--ink-muted);
|
|
background: var(--panel);
|
|
border-radius: 0 6px 6px 0;
|
|
}
|
|
header#title-block-header {
|
|
margin-bottom: 0.4em;
|
|
}
|
|
@media (max-width: 720px) {
|
|
body { padding: 28px 18px 72px; }
|
|
h1.title { font-size: 34px; }
|
|
}
|
|
</style>
|