- Store session tokens server-side on login (was: generated but not stored)
- Add /api/v1/auth/logout endpoint for server-side session invalidation
- Delete old sessions on login to prevent session fixation attacks
- Add Cache-Control: no-store headers to all auth responses
Security fixes:
1. Session identifiers now rotated on login (old sessions deleted)
2. Logout properly invalidates server-side session
3. Auth responses include anti-caching headers
Critical fixes:
- CR-001: Remove auth backdoor code 250365 (lib/dbcore.go)
High fixes:
- HI-001: Fix CORS wildcard to use origin allowlist (portal/api_mobile.go, portal/mcp_http.go)
- HI-002: Fix LOINC skip logic - skip only if BOTH SearchKey2 AND LOINC are set (lib/normalize.go)
Also added:
- Full code review report at docs/CODE-REVIEW-2026-02-28.md
14 issues found: 2 critical, 4 high, 5 medium, 3 low
3 fixes applied, remaining are documented for follow-up
- Add import-renpho: Go binary to sync Renpho body composition data
into inou vitals via direct EntryWrite (AES-128/ECB Renpho API client,
13 body metrics, dedup, auto RBAC grants, -setup/-discover modes)
- Add POST /api/v2/readings endpoint for batch vital ingest
- Fix dashboard lab chip: filter by lab_order not lab_report
- Portal: upload handler, dossier page rework, dashboard updates
- Remove tools/fix-lang (replaced by toolkit translate)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix lab chart reference bands: parse DOB in DossierFromEntry, generate
deterministic ref_ids in import-caliper (was collapsing 4363 rows to 1)
- Consolidate DossierFromEntry into lib/dbcore.go (eliminate portal duplicate)
- Add Import field to entries for batch undo (NextImportID, all import paths)
- MyChart direct JSON parsing (skip Gemini for structured lab data)
- Multi-order extraction from markdown/text tables
- Normalize progress callback for UI feedback
- DICOM import, genome import, API, portal, MCP, translation updates
- Remove test DICOM data from repo
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
api-docs.txt: token is NOT the dossier ID — it's a time-limited encrypted
string. Point users to /connect for token generation.
install_public.tmpl: Grok prompt now points to /connect (Grok tab) for
the token, not /dashboard which doesn't show it.
- Remove inou_bridge_win/darwin download links (fully web-based MCP now)
- Mirror English connect.tmpl structure exactly
- Proper Dutch and Russian translations throughout
- Claude Desktop, Grok, ChatGPT, and Other AI tabs all translated
- Functional prompts (custom instructions, Grok API prompt) kept in English
Reference data simplification (choke point pattern):
- Remove RefSave/RefDelete from lib (import-time only, not runtime)
- Remove LabTestSave*, LabRefSave* from lib/lab_reference.go
- Remove PopulateReferences (LLM-based ref generation)
- Keep only RefQuery() for runtime reads
- Import tools handle their own SQL inserts
Rate limiting removed:
- Delete new_signups table and all rate limit code
- Solved via different approach (not in codebase)
Database consolidation (on staging):
- Moved genotypes table (30K SNPs) to reference.db
- Deleted empty DBs: portal.db, rate_limit.db, snpedia.db, ratelimit.db
Net -293 lines. Runtime code now only reads reference data via RefQuery().
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Simplify access control from 500+ lines to ~50 lines of core logic:
- New permission bitmask (PermRead/Write/Delete/Manage)
- Hierarchical access (dossier → category → entry)
- Single choke points: CheckAccess(), EntryQuery(), DossierQuery()
- All data access now enforced through lib RBAC layer
- Removed complex role templates and permission caching
Also improved NewID() to use UUID v4 + SHA-256 hash for better
randomness distribution (was limited to 0-7 hex start).
Net -210 lines across 28 files. Ready for staging deployment.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
list_dossiers, list_studies, list_series, list_slices, query_entries,
get_categories, query_genome — all now call lib directly with
AccessContext{AccessorID: dossierID}. No more HTTP roundtrip to the
internal API with its separate auth path.
Image and journal tools still use API (image rendering logic lives
there, and the API already enforces RBAC via lib.CheckAccess).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Rename Query→dbQuery, Save→dbSave, Load→dbLoad, Delete→dbDelete,
Count→dbCount in lib/db_queries.go. Go compiler now prevents any code
outside lib/ from bypassing RBAC checks.
All external callers migrated to RBAC-checked functions:
- EntryCategoryCounts, EntryCount, EntryListByDossier (new)
- LabTestList, LabEntryListForIndex, LabRefLookupAll (new)
- GenomeQuery now requires AccessContext
- EntryDeleteByCategory/EntryDeleteTree now require AccessContext
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Replace N separate SQL queries with single IN clause for rsids and genes
- Dedup results by rsid, merging categories from multiple tiers
- Add repute filter (Good/Bad/Clear) to genome queries
- Expose limit/offset as MCP parameters
- Add genotype to search check
- Fix category filter in genomeEntriesToResult
- Remove deprecated api/api_categories.go and api/api_genome.go
- Change GenomeMatch to use Categories []string instead of Category+Subcategory
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Move GenomeQuery logic from api/api_genome.go to lib/v2.go so MCP
handler calls lib directly instead of HTTP round-trip (fixes 403 on
genome queries via Claude.ai MCP - was hitting RBAC table mismatch)
- Generate CategoryFromString from categoryNames in init() (single
source of truth, removes 9 unused aliases)
- Redact sensitive variants (Bad repute, magnitude >4) in targeted
queries: genotype/summary replaced with "hidden" + hint to use
include_hidden=true. Broad queries still suppress entirely.
- API handler is now a thin wrapper parsing query params
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Replace specific DPO name with generic privacy email across all legal templates
- Update DPA to clarify third-party services vs sub-processors distinction
- Add privacy policy and DPA cross-references in Terms
- Add intellectual property section to Terms
- Improve prompts UI with Yes/No buttons, section headers, and better visual hierarchy
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Add /legal/terms with comprehensive ToS content
- Add terms link to footer navigation
- Add /legal/terms to defense.go whitelist for external access
- Update privacy policy and DPA templates with improved styling
- Refactor RBAC editor template formatting
- Add prompts AI setup documentation
- Include database migration scripts
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Removed the backward compatibility fallback that checked the old
dossier_access table from CanManageDossier/CanAccessDossier - it was
a security risk (hidden path that bypassed the new RBAC system).
Instead, added MigrateOldAccess() that converts existing dossier_access
entries to proper access grants on startup (idempotent - skips existing).
Migration rules:
- Self-references (accessor == target) skipped (owner access is automatic)
- can_edit = 1 → "rwdm" root grant
- can_edit = 0 → "r" root grant
- Role set to "Migrated" for tracking
Result: 12 grants migrated from old table.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Updated "Edit" button in dossier Privacy section to open new RBAC editor
instead of old access editor. Users can now access granular per-category
permissions directly from the dossier page.
Location: Privacy section → Edit button next to each person with access
Route changed: /dossier/{id}/access/{grantee_id} → /dossier/{id}/rbac/{grantee_id}
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Changed from empty accessorID bypassing checks to explicit SystemAccessorID
for better security and audit trail.
Before: accessorID == "" → bypass all checks (security risk)
After: accessorID == "system-internal" → bypass (explicit, auditable)
Changes:
- Added SystemAccessorID constant = "system-internal"
- Updated SystemContext to use SystemAccessorID
- Updated checkAccess() to check for specific ID
- Updated accessorIDFromContext() to return SystemAccessorID
- Updated all EntryList calls to use SystemAccessorID
- Updated auth.go helpers to use SystemAccessorID
Benefits:
- Explicit backdoor ID visible in audit logs
- No accidental bypass from empty strings
- Clear intent for system operations
- Can't collide with real hex dossier IDs (uses "system" prefix)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Complete refactor from AccessContext struct to simple accessorID string
parameter for RBAC enforcement, as requested. All access control remains
in lib layer - API/Portal just pass accessor + dossier to lib functions.
Changes:
- Added accessorIDFromContext() helper in lib/v2.go
- Updated all checkAccess() calls to extract accessorID from context
- Updated all EntryList() calls (nil → "" for system context)
- Fixed auth.go helper functions to extract accessorID
- Updated categories API to pass accessor through to lib
All RBAC enforcement stays in lib - no API-level access checks.
Empty accessorID bypasses checks (system/internal operations).
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Updated all genome functions to accept AccessContext parameter
- GenomeGetExtraction, GenomeGetTiers, GenomeGetTierByCategory,
GenomeGetVariants, GenomeGetVariantsByTier now pass context to EntryList
- API genome handler uses system context after dossier access check
- Categories endpoint uses system context for counting operations
- Fixes MCP query_genome returning 403/no data errors
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- New dossier_sections.go with DossierSection struct and BuildDossierSections()
- Single section_block template replaces 12+ copy-pasted HTML blocks
- All 26 categories supported with default handler for unknown ones
- /dossier/{id} now uses v2, /dossier/{id}/v1 keeps legacy
- Added missing translation keys for all section types
- CSS: added .section-children and .hidden-row classes