REST API with endpoints for threads, replies, nodes, and email OTP
authentication. Includes in-memory sliding-window rate limiting,
global api.enabled INI kill-switch, per-namespace api_access opt-out
with settings UI checkbox, and 500k character content limit (~128k
tokens) for long-form agent content. Ships enabled by default.
- Normalize emails to lowercase on user creation
- Use case-insensitive lookup in get_user_by_email
- Add merge_duplicate_email_users script to merge existing duplicates
- Add unit and integration tests to prevent regression
- Update CLAUDE.md with tmux-hosts production troubleshooting
Implements per-worker database isolation for parallel test runs:
- Add pytest-xdist and pytest-cov to test dependencies
- Configure Makefile to run tests with -n auto flag
- Update test.ini to use environment variable for database path
- Create conftest.py with per-worker database isolation
- Enable SQLite WAL mode for better concurrency
- Auto-cleanup test databases after completion
Expected performance improvement similar to make_post_sell (~16x speedup).
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Add google_site_verification column to rb_namespace table
- Add settings form field in Stand-alone Mode Settings
- Render meta tag on root path when set
- Update CLAUDE.md with Alembic migration workflow
- Fix development.ini alembic paths
Tests cover:
- Anonymous reply creates UserSurrogate
- Anonymous reply with no name defaults to 'Anonymous'
- Regular namespace still requires email
- Anonymous comments are marked as verified
- Namespace settings toggle for allow_anonymous
When enabled, commenters can post with just a display name (no email required).
Comments are attached to UserSurrogate instead of User.
Trade-offs for anonymous commenters:
- No email notifications for replies
- Cannot edit their comments
- Cannot log in to manage comments
- No cross-site identity
Works with existing moderation (hide_unless_approved) - anonymous
comments are never auto-approved when moderation is enabled.
- Replace multiple forms with single unified payment form
- Remove deprecated pay_what_you_can preferences (no longer needed with Stripe Checkout)
- Remove pay-what-you-can route and view
- Simplify setup-namespace to just link to billing
- Update tests for new billing UI
- Remove User.stripe_id column (no longer used with Stripe Checkout)
- Show billing link to all authenticated users in phone menu
- Remove old Stripe customer cleanup from tests
- Add test_stripe.py with unit tests for checkout module and Payment model
- Add functional tests for billing views (pay-what-you-can, checkout, success)
- Fix mock imports for Python 3 compatibility (unittest.mock)
- Remove unused stripe.j2 imports from templates
- Remove jQuery (84KB) - all functionality now vanilla JS
- Remove legacy google-analytics.j2 (using gtag v4 instead)
- Remove ie8.polyfils.min.js (IE8 is dead)
- Add X-Requested-With header for AJAX preview requests
- Textareas auto-grow up to 400px as content is added
- Auto-grow triggers on toggle open if textarea has content
- Use CSS keyframes (slideDown/slideUp) for 800ms door-like animations
- JS only toggles classes and updates button text at correct timing
- Preview toggle uses native <details> with animated open/close
- Arrow indicators on right side (hide preview ▲ / show preview ▼)
- No-JS fallback preserved (links navigate to dedicated pages)
- CSS grid animation for smooth expand/collapse transitions
- Fade-in animation for namespace dropdown
- Auto-focus textarea on details open (progressive enhancement)
- Falls back gracefully if browser lacks support