10 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Project Overview
SeaFare Montana is a maritime logistics AI agent platform. A single-page web app with a Flask API backend, Claude API agent with tool-calling, and PostgreSQL database (SQLite in dev). Deployed on Render.com free tier (Python 3.11) with auto-deploy from master.
Commands
# Run locally
python seafare_api.py # Starts Flask on port 5050
# Production (Render)
gunicorn seafare_api:app --bind 0.0.0.0:$PORT --workers 1 --threads 2 --timeout 120
# Check Python syntax before committing (all core files)
python -m py_compile seafare_api.py && python -m py_compile seafare_agent.py && python -m py_compile maritime_db.py && python -m py_compile moltbook_bot.py && python -m py_compile telegram_bot.py && python -m py_compile ais_provider.py && python -m py_compile equasis_parser.py && python -m py_compile marinetraffic_parser.py && python -m py_compile maritime_compliance.py
# Run tests
python -m pytest tests/ -v --tb=short
# Deploy: push to master triggers Render auto-deploy
git add <files> && git commit -m "message" && git push
Pre-commit hook runs automatically: syntax check → pytest → .env secret guard.
Architecture
index.html ─── Single-page frontend (vanilla JS, Leaflet map, no framework)
↓ fetch /api/v1/*
seafare_api.py ─── Flask REST API (auth, wallet, chat, admin endpoints)
↓ generate_response()
seafare_agent.py ─── Claude API agent with 26 tools (tool-calling loop, max 7 iterations)
↓ _TOOL_DISPATCH dict → handler functions
maritime_db.py ─── Database layer (19 tables, PostgreSQL/SQLite dual-mode)
↑ external data
config.py ─── Centralized constants (version, rate limits, cache TTLs, fees, env var names)
marinetraffic_parser.py ─── Ports DB (16,553 ports) + routing engine + cargo classification + premium tool helpers
ais_provider.py ─── Unified AIS: AISStream WebSocket + AISHub REST + Digitraffic REST + MT fallback
equasis_parser.py ─── IMO vessel registry (authenticated web scraping)
maritime_compliance.py ─── Sanctions screening + dark fleet detection (standalone, no external deps)
moltbook_bot.py ─── Promotional bot for Moltbook platform (background daemon)
telegram_bot.py ─── Telegram bot: standalone service, group chats, vessel watch alerts
AI Agent (seafare_agent.py)
Uses Claude claude-sonnet-4-5-20250929 with tool-calling. The agent loop sends user message → Claude responds with tool_use blocks → _execute_tool_inner() runs them via _TOOL_DISPATCH dict → results sent back → repeats until stop_reason != "tool_use" (max 7 iterations via AGENT_MAX_TOOL_ITERATIONS). System prompt is action-oriented: agent detects request type and uses tools immediately, no filler text.
Tools (26): search_vessel, get_vessel_details, get_position, search_vessels_near_port, calculate_route, find_vessels_for_cargo, calculate_demurrage, search_contacts, unlock_contacts, get_freight_rate, screen_sanctions, check_port_congestion, get_bunker_prices, optimize_bunker, generate_charter_party, vessel_performance, generate_bill_of_lading, optimize_crew_change, calculate_insurance, estimate_port_costs, weather_routing, generate_fixture_recap, detect_ais_anomaly, detect_dark_fleet, search_web, get_revenue
user_context (profile summary + balance) is injected into the system prompt so the agent knows who it's talking to.
AIS Data Priority Chain (ais_provider.py)
For single vessel position (get_vessel_position):
- DB cache — fresh position (< 5 min)
- AISStream — persistent background thread OR sync fallback (8s WebSocket query in request thread)
- AISHub REST — on-demand query (1 min rate limit)
- Digitraffic — Finnish/Baltic waters (free, no key)
- MarineTraffic — scraping fallback
- Stale DB — old cache as last resort
Important: Daemon threads die on Render's gunicorn gthread workers. Sync fallback (query_position_sync, query_area_sync) runs inside Flask request threads and works reliably.
Vessel Search Features
Both search_vessels_near_port and find_vessels_for_cargo use expanding radius search:
- Radius steps: 50 → 200 → 500 NM. First step uses full AIS provider (slow, ~6-8s). Wider steps use DB-only queries (instant).
- Always returns at least 3 vessels (MIN_VESSELS). Each vessel has
distance_nmandmap_link. map_linkformat:{{SHOWMAP~lat~lon~zoom~name}}— rendered as clickable buttons in chat byformatResponse()step 7 inindex.html. Uses~delimiter (not|) to avoid markdown table conflicts.
Auth System (seafare_api.py)
Hybrid stateless + stateful: primary auth uses itsdangerous signed tokens (30-day TTL, survives Render DB wipes). Fallback to legacy UUID DB sessions. auto_promote_admin() checks ADMIN_EMAILS env var on every auth.
Payment Flow
search_contacts → free masked preview → user confirms → unlock_contacts → charge_user() (atomic SQL WHERE balance >= ?) → add_service_charge() → save_purchased_contacts(). Previously purchased contacts are returned free via has_purchased_contact().
i18n (Dual)
- Server-side:
TEXTSdict inseafare_api.py+L(lang, key)helper — for API error messages - Client-side:
i18nJS object inindex.html+t(key)helper — for all UI labels. Three languages: EN, RU, ES. Always add keys to all three.
Background Threads (seafare_api.py)
Two daemon threads auto-start at module level:
- Keep-alive (
_start_keep_alive): pings/healthevery 14 min to prevent sleep - Moltbook bot (
_start_moltbook_bot): searches maritime posts on Moltbook every 30 min, generates expert comments via Claude Haiku
Telegram Bot (telegram_bot.py — separate systemd service)
Runs as standalone process (seafare-telegram.service), NOT inside gunicorn (daemon threads die on fork with --preload).
Features: Full AI agent (26 tools), group chat support (@mention/reply/commands), vessel watch alerts (/watch, /unwatch, /watches), auto language detection, per-user rate limiting in groups.
Vessel Watch: Background checker thread polls DB positions every 10 min. Arrival watches (one-shot) fire when vessel enters port radius. Status watches (continuous) fire on nav status changes. Max 5 watches per chat, auto-expire 7 days. DB table: vessel_watches.
Adding New Tools
- Add helper function in
marinetraffic_parser.py(ormaritime_compliance.pyfor compliance tools) - Add tool definition to
TOOLSlist inseafare_agent.py(withinput_schema) - Add handler function
_tool_<name>()inseafare_agent.py - Add entry to
_TOOL_DISPATCHdict inseafare_agent.py - Add trigger rule in
SYSTEM_PROMPTBEHAVIOR section - Add UI service card in
index.html+ i18n keys in all 3 languages (EN/RU/ES)
Key Conventions
- Versioning:
APP_VERSIONinconfig.py(semver). Bump with every commit: PATCH for fixes, MINOR for features, MAJOR for breaking changes. Displays in/healthendpoint and sidebar footer. - Config centralization: All constants live in
config.py(rate limits, cache TTLs, fees, env var names). Import from there, don't hardcode. - Data sources are secret: System prompt instructs agent to never reveal Equasis/MarineTraffic. Present data as "our maritime intelligence network."
- Admin:
is_adminusers get free access to all paid features. Check viais_adminflag on user dict. - Atomic balance operations: Always use
charge_user()(returns bool) — never manually subtract balance. - DB migrations: Use
CREATE TABLE IF NOT EXISTSandCREATE INDEX IF NOT EXISTSininit_db(). No migration framework — backward-compatible additions only. - .env contains secrets — never commit. Credentials are set via Render dashboard env vars in production.
- Single HTML file: All CSS, HTML, and JS live in
index.html. No build step, no bundler. - Port database:
world_ports.json— 16,553 ports from UN/LOCODE. Regenerate withpython merge_unlocode.py. - Cargo classification:
CARGO_TO_VESSELdict inmarinetraffic_parser.pymaps cargo types → vessel categories (bulk, tanker, container, roro, general).
Database (PostgreSQL + SQLite)
Dual-mode: maritime_db.py auto-detects DATABASE_URL env var → PostgreSQL in production, SQLite locally. Wrapper classes (_PgCursorWrapper, _PgConnectionWrapper) translate SQLite-style ? placeholders to %s and auto-add RETURNING id to INSERTs. Always write SQL with ? placeholders — the wrapper handles conversion.
Tables (22): vessels, positions, contacts, port_calls, demurrage, users, sessions, chat_history, user_profiles, wallets, deposits, withdrawals, service_charges, purchased_contacts, port_vessel_cache, equasis_cache, equasis_daily_counter, query_log, moltbook_comments, user_memory, conversation_summaries, vessel_watches
All user-related tables FK to users(id). Vessel-related tables use mmsi as key.
Environment Variables
| Variable | Purpose |
|---|---|
ANTHROPIC_API_KEY |
Claude API (required for AI agent + Moltbook bot) |
EQUASIS_USER / EQUASIS_PASS |
Equasis.org login |
GOOGLE_CLIENT_ID |
Google OAuth |
ADMIN_EMAILS |
Comma-separated admin emails (auto-promoted on login) |
SECRET_KEY |
Token signing (falls back to ANTHROPIC_API_KEY) |
MOLTBOOK_API_KEY |
Moltbook platform API key (bot disabled if not set) |
AISSTREAM_API_KEY |
AISStream.io WebSocket API key (real-time AIS) |
AISHUB_USERNAME |
AISHub.net contributor username (REST AIS queries) |
DIGITRAFFIC_ENABLED |
'1' (default) to enable Finnish Digitraffic AIS, '0' to disable |
TELEGRAM_BOT_TOKEN |
Telegram Bot API token from @BotFather (bot disabled if not set) |
DATABASE_URL |
PostgreSQL connection string (auto-set by Render; if absent → SQLite) |