Skip to content

markmhendrickson/neotoma

v0.12.0 Security

This release includes 3 security fixes for security teams reviewing exposed deployments.

Published 22d MCP Developer Tools
✓ No known CVEs patched
Read the diff → Tool health → What is this tool? →
This release patches 3 known CVEs

Topics

agent-infrastructure agent-memory ai-agents deterministic-state entity-resolution event-sourcing
+3 more
mcp privacy-first provenance

Affected surfaces

auth breaking_upgrade

ReleasePort's take

Light signal
editorial:auto 13d

v0.12.0 removes the legacy feedback subsystem entirely; all applications must migrate to the new issues API. MCP submit_issue now requires reporter_git_sha or reporter_app_version environment metadata.

Why it matters: Feedback subsystem is removed entirely—migrate workflows to issues API before upgrading. MCP clients must pass reporter_git_sha or reporter_app_version. Test in dev first.

Summary

AI summary

MCP submit_issue now requires reporter environment (git SHA or app version).

Changes in this release

Security Medium

OAuth Bearer token validation on /mcp; invalid tokens return 401

OAuth Bearer token validation on /mcp; invalid tokens return 401

Source: llm_adapter@2026-05-21

Confidence: high

Security Medium

Inbound peer sync webhook validates sender_peer_url and rejects private hosts

Inbound peer sync webhook validates sender_peer_url and rejects private hosts

Source: llm_adapter@2026-05-21

Confidence: high

Security Medium

Guest access tokens carry explicit TTL; default 30 days with revocation

Guest access tokens carry explicit TTL; default 30 days with revocation

Source: llm_adapter@2026-05-21

Confidence: high

Security Medium

Public issue submission redaction prevents PII; ISO dates no longer false-positive

Public issue submission redaction prevents PII; ISO dates no longer false-positive

Source: llm_adapter@2026-05-21

Confidence: high

Security Medium

Guest write-rate limiter for issue/subscription operations; default 30/min

Guest write-rate limiter for issue/subscription operations; default 30/min

Source: llm_adapter@2026-05-21

Confidence: low

Security Medium

isLocalRequest hardened; production loopback non-local by default

isLocalRequest hardened; production loopback non-local by default

Source: llm_adapter@2026-05-21

Confidence: low

Security Medium

Guest write‑rate limiter added for issue/subscription ops, default 30 requests per minute, keyed by AAuth thumbprint > token > IP

Guest write‑rate limiter added for issue/subscription ops, default 30 requests per minute, keyed by AAuth thumbprint > token > IP

Source: granite4.1:30b@2026-05-23-audit

Confidence: low

Breaking Medium

Legacy feedback subsystem completely removed; issues subsystem is replacement

Legacy feedback subsystem completely removed; issues subsystem is replacement

Source: llm_adapter@2026-05-21

Confidence: low

Breaking Medium

Access policy source precedence: env > SchemaMetadata.guest_access_policy > config

Access policy source precedence: env > SchemaMetadata.guest_access_policy > config

Source: llm_adapter@2026-05-21

Confidence: low

Breaking Medium

MCP submit_issue requires reporter_git_sha or reporter_app_version

MCP submit_issue requires reporter_git_sha or reporter_app_version

Source: llm_adapter@2026-05-21

Confidence: low

Feature Medium

Substrate subscriptions via webhook and SSE for entity/event change watching

Substrate subscriptions via webhook and SSE for entity/event change watching

Source: llm_adapter@2026-05-21

Confidence: high

Feature Medium

Reporter environment metadata propagates through issue messages and store

Reporter environment metadata propagates through issue messages and store

Source: llm_adapter@2026-05-21

Confidence: high

Feature Medium

MCP profile-aware port-file mode enabled by NEOTOMA_MCP_LOCAL_HTTP_PORT_PROFILE

MCP profile-aware port-file mode enabled by NEOTOMA_MCP_LOCAL_HTTP_PORT_PROFILE

Source: llm_adapter@2026-05-21

Confidence: high

Feature Medium

neotoma processes servers shows deduplicated server view with LaunchAgent info

neotoma processes servers shows deduplicated server view with LaunchAgent info

Source: llm_adapter@2026-05-21

Confidence: high

Feature Medium

Schema boot seeding adds plan entity with guest-access policies

Schema boot seeding adds plan entity with guest-access policies

Source: llm_adapter@2026-05-21

Confidence: high

Feature Medium

neotoma doctor detects iCloud-synced and risky data directory patterns

neotoma doctor detects iCloud-synced and risky data directory patterns

Source: llm_adapter@2026-05-21

Confidence: high

Feature Medium

Issues subsystem replaces feedback with GitHub integration and bulk operations

Issues subsystem replaces feedback with GitHub integration and bulk operations

Source: llm_adapter@2026-05-21

Confidence: low

Feature Medium

Cross-instance peer sync subsystem: add/remove/sync peers with conflict resolution

Cross-instance peer sync subsystem: add/remove/sync peers with conflict resolution

Source: llm_adapter@2026-05-21

Confidence: low

Feature Medium

27 new HTTP API operations for issues, subscriptions, peers, lifecycle, schema

27 new HTTP API operations for issues, subscriptions, peers, lifecycle, schema

Source: llm_adapter@2026-05-21

Confidence: low

Bugfix Medium

Dev/prod APIs canonically bind ports 3080/3180 with incumbent pre-kill

Dev/prod APIs canonically bind ports 3080/3180 with incumbent pre-kill

Source: llm_adapter@2026-05-21

Confidence: high

Bugfix Medium

canonicalAauthAuthority uses NEOTOMA_PUBLIC_BASE_URL for hosted/tunnelled signing

canonicalAauthAuthority uses NEOTOMA_PUBLIC_BASE_URL for hosted/tunnelled signing

Source: llm_adapter@2026-05-21

Confidence: high

Bugfix Medium

MCP stdio shims source shared helpers; prevent cross-profile URL leakage

MCP stdio shims source shared helpers; prevent cross-profile URL leakage

Source: llm_adapter@2026-05-21

Confidence: high

Bugfix Medium

MCP stale session returns 503; uninitialized request returns 400

MCP stale session returns 503; uninitialized request returns 400

Source: llm_adapter@2026-05-21

Confidence: low

Bugfix Medium

LaunchAgent reload no longer orphans API; uses process-group SIGTERM

LaunchAgent reload no longer orphans API; uses process-group SIGTERM

Source: llm_adapter@2026-05-21

Confidence: low

Bugfix Medium

MCP handles stale HTTP sessions: missing session returns 400, unknown/expired session returns 503 with clear error codes

MCP handles stale HTTP sessions: missing session returns 400, unknown/expired session returns 503 with clear error codes

Source: granite4.1:30b@2026-05-23-audit

Confidence: low

Full changelog

Install

npm install -g [email protected]

| | |
|:--|:--|
| npm | https://www.npmjs.com/package/neotoma/v/0.12.0 |
| Compare | v0.11.1v0.12.0view diff |

v0.12.0 turns Neotoma into a multi-instance State Layer. Substrate subscriptions, peer-to-peer sync, and a first-class issues subsystem replace the legacy feedback path; the auth hardening introduced in v0.11.1 is carried forward here and extended with pre-release gates G1–G5, stricter proxy/loopback handling, and clearer operator guidance; and the dev/prod LaunchAgents, MCP stdio shims, and CLI surfaces are reworked to stop orphaning processes, stop drifting off canonical ports, and give operators a real processes servers view, plans capture/list, and a doctor that flags risky data directories.

Highlights

  • Cross-instance peer sync becomes a first-class subsystem. add_peer / list_peers / get_peer_status / remove_peer / sync_peer ship with POST /sync/webhook inbound delivery (HMAC-signed X-Neotoma-Sync-Signature-256), AAuth-thumbprint loop prevention, and resolve_sync_conflict for prefer-local vs prefer-remote reconciliation. get_peer_status now includes remote_health (/health probe + neotoma compat semver check) so operators can see immediately when a peer is offline or runs an incompatible version. applyInboundSyncWebhook rejects mismatched sender_peer_url values and blocks private/loopback hostnames when NEOTOMA_HOSTED_MODE=1. See docs/subsystems/peer_sync.md.
  • Substrate subscriptions with webhook + SSE transport. subscribe / unsubscribe / list_subscriptions / get_subscription_status plus GET /events/stream (SSE) let agents and external services watch entity / event / relationship changes for specific entity_types, entity_ids, or event_types. Webhook deliveries are HMAC-signed; SSE uses the same auth as the API. Loop-prevention sync_peer_id is wired through both transports.
  • Issues replace the feedback subsystem end-to-end. submit_issue, add_issue_message, get_issue_status, sync_issues, bulk_close_issues, bulk_remove_issues are the canonical reporting flow, with GitHub mirror + operator forwarding + guest read-back tokens. Public submit + public-thread message paths run through the existing scanAndRedact PII guard as a backstop, ISO-date literals no longer false-positive as phone numbers, and loadIssueThreadMessages deduplicates across remote + local + multi-conversation threads. New CI workflows auto-label issues from issue_kind and post upgrade-guidance comments on releases.
  • Reporter environment is required on submit_issue, soft-required on add_issue_message. The MCP submit_issue JSON Schema gains anyOf: [{ required: [reporter_git_sha] }, { required: [reporter_app_version] }], so MCP clients fail validation locally before sending; the HTTP POST /issues/submit route returns 400 ERR_REPORTER_ENVIRONMENT_REQUIRED with details.acceptable_field_groups. On public add_issue_message threads, the server emits a stderr warning but still persists the message.
  • Security follow-through after v0.11.1. This branch carries forward the auth-bypass hardening from the v0.11.1 hotfix line and adds the work that makes it operationally durable: isLocalRequest keeps the stricter forwarded-hop handling, production loopback remains non-local by default (set NEOTOMA_TRUST_PROD_LOOPBACK=1 to opt back in), a new CI security_gates job runs G1 (security:classify-diff), G2 (security:lint), G3 (security:manifest:check + test:security:auth-matrix) on every PR, the sandbox weekly reset workflow now runs G5 deployed probes against https://sandbox.neotoma.io, SECURITY.md is rewritten with the disclosure flow and advisories index, and docs/developer/mcp/proxy.md documents the operator-required MCP_PROXY_FAIL_CLOSED=1 for AAuth-signed hosted deployments.
  • Operator security middleware. A dedicated guest write-rate limiter keys by AAuth thumbprint → hashed guest token → IP (NEOTOMA_GUEST_WRITE_RATE_LIMIT_PER_MIN, default 30/min). /mcp now validates OAuth Bearer tokens and responds with a proper 401 invalid_token envelope and WWW-Authenticate resource-metadata header when the token is unknown or expired. Guest access tokens carry an explicit TTL (NEOTOMA_GUEST_TOKEN_TTL_SECONDS, default 30 days) and a revoked_at field, and persistence errors are no longer swallowed silently.
  • LaunchAgent and MCP shim reliability. The dev-server chokidar watcher now spawns the API as a process-group leader and SIGTERMs the whole group on reload, eliminating the orphan-process explosion that accumulated 50+ stale dev API instances on ports 3145–3179. Both LaunchAgents pre-kill incumbents on 3080 / 3180 so dev and prod always bind their canonical ports. scripts/reload_neotoma_launchagents.sh --kill-zombies recognizes the actual orphan patterns observed in the wild. All MCP stdio shim wrappers (signed / unsigned, dev / prod, Cursor / Claude Code / Codex) source the new shared helpers (scripts/lib/neotoma_mcp_source_env.sh, scripts/lib/neotoma_mcp_resolve_downstream_url.sh), the resolver treats an explicit NEOTOMA_MCP_LOCAL_HTTP_PORT_PROFILE=dev|prod as sufficient to enable port-file mode (Cursor strips "1" flag values), and the downstream resolver warns on (but never honors) cross-profile MCP_PROXY_DOWNSTREAM_URL leakage.
  • CLI: new processes servers, plans capture/list, access list/set/reset, doctor risk detection. neotoma processes servers collapses parent-child process trees into one row per listener, surfaces LAUNCHAGENT / DATA_DIR / LOGS, and supports --watch. neotoma plans capture <file> / --all ingests harness .plan.md files (.cursor/plans, .claude/plans, .codex/plans, .openclaw/plans) via the canonical combined store path. neotoma access list shows the winning policy source (env / schema_metadata / config_file / default); access set / reset / enable-issues / disable-issues write through SchemaMetadata.guest_access_policy as the canonical source (the legacy config file is now a deprecated fallback only). neotoma doctor --json adds data.risks for iCloud-synced / Documents / Desktop data directories and prior SQLite repair artifacts, plus a suggested_safe_data_dir.
  • OpenClaw plugin entry point. A new src/openclaw_entry.ts registers Neotoma MCP tools as OpenClaw agent tools, declares kind: "memory" so users can wire it via plugins.slots.memory = "neotoma", and persists session lifecycle events (session.start, session.end, afterToolCall) into Neotoma using the same store-first contract as MCP.
  • Site, Inspector, agent-instruction expansion. ~80 new MDX pages under docs/site/pages/en/ cover personas (CRM, customer-ops, financial-ops, government, healthcare, logistics, compliance, diligence, crypto-engineering), comparisons (vs database, vs files, vs mem0, vs platform memory, vs RAG, vs Zep), integrations (Claude / Claude Code / Codex / Cursor / ChatGPT remote MCP), and risk/guarantee primitives (auditable change log, deterministic state evolution, human inspectability, conflicting-facts risk, false-closure risk, memory guarantees, non-destructive testing). The Inspector submodule advances to its peers + access-policy UI commit. Agent-facing instructions (docs/developer/mcp/instructions.md, docs/specs/MCP_SPEC.md, docs/developer/cli_reference.md) gain Inspector-linked display headings, an ISSUE REPORTING section keyed off issues.reporting_mode (proactive / consent / off), and explicit reporter-env requirements.

What changed for npm package users

MCP tools

  • New: subscribe, unsubscribe, list_subscriptions, get_subscription_status, add_peer, list_peers, get_peer_status, remove_peer, sync_peer, resolve_sync_conflict, bulk_close_issues, bulk_remove_issues, restore_entity, restore_relationship, analyze_schema_candidates, get_schema_recommendations, update_schema_incremental.
  • submit_issue: required anyOf: [reporter_git_sha, reporter_app_version] at the JSON Schema level. Accepts visibility: "advisory" as a hidden alias for "private" for one minor release; response includes _deprecation: "visibility 'advisory' is deprecated; use 'private' instead.". Per-row data_source and external_actor propagated through the store contract.
  • add_issue_message: accepts reporter_git_sha, reporter_git_ref, reporter_channel, reporter_app_version. On public threads the server emits a stderr warning when both reporter_git_sha and reporter_app_version are missing; the message still persists.
  • get_issue_status / add_issue_message: both accept either entity_id or issue_number via anyOf (issue_number is an integer).
  • store: request shape gains optional external_actor (GitHub upstream author for sync-replayed observations) and source_peer_id (peer that originated the observation). observation_source enum gains "sync" for cross-instance replayed writes.
  • /mcp Bearer auth: invalid or expired OAuth tokens now return 401 invalid_token with a WWW-Authenticate header and a JSON-RPC error envelope instead of silently falling through to anonymous.

HTTP API

  • 27 new operations added since v0.11.1. Issues: issuesSubmit, issuesAddMessage, issuesGetStatus, issuesSync, bulkCloseIssues, bulkRemoveIssues. Subscriptions: subscribe, unsubscribe, listSubscriptions, getSubscriptionStatus, eventsStream (SSE). Peers + sync: addPeer, listPeers, getPeerStatus, removePeer, syncPeer, applySyncWebhook, listSyncEntities, resolveSyncConflict. Lifecycle: deleteEntity, deleteRelationship, restoreEntity, restoreRelationship. Schema: analyzeSchemaCandidates, getSchemaRecommendations, updateSchemaIncremental. Access policy: getAccessPolicies.
  • POST /issues/submit: the description now declares the reporter-environment requirement, and the 400 response declares error_code: ERR_REPORTER_ENVIRONMENT_REQUIRED with details.acceptable_field_groups: [["reporter_git_sha"], ["reporter_app_version"]]. (HTTP enforcement is at the application layer; the JSON Schema body is unchanged, so npm run openapi:bc-diff reports this as non-breaking. MCP enforcement is at the JSON Schema layer via anyOf — see Breaking changes below.)
  • POST /issues/add_message: accepts reporter_git_sha, reporter_git_ref, reporter_channel, reporter_app_version (all optional; soft requirement on public threads).
  • POST /store: ErrorEnvelope.error_code enum gains ERR_RELATIONSHIP_ENTITY_ID_FORMAT so structured-store callers can detect malformed source_entity_id / target_entity_id formats with a specific code + hint instead of a generic VALIDATION_INVALID_FORMAT.
  • Guest write paths (/issues/submit, /issues/add_message, /subscribe, /unsubscribe) share a separate rate-limit bucket from the broader /store limiter and consume a guest budget keyed by AAuth thumbprint > hashed guest token > IP.

CLI

  • neotoma processes servers — server-only filtered view of Neotoma processes, deduplicated by parent-child stack, with LAUNCHAGENT / DATA_DIR / LOGS columns and --watch mode.
  • neotoma plans capture <file> / --all — ingest harness .plan.md files via the canonical combined store path (raw markdown + plan row + EMBEDS). Identity is keyed by (harness, harness_plan_id), so re-running is idempotent.
  • neotoma plans list [--source-entity-id] [--status] [--harness] [--limit] — list stored plan entities; wraps POST /retrieve_entities.
  • neotoma access list — shows the winning source per entity type (env, schema_metadata, config_file, or default); access reset reports effective_mode / effective_source after reset so an env override that still wins is visible.
  • neotoma issues create --advisory is a deprecated hidden alias for --visibility private (one minor release). neotoma issues status now accepts --issue-number as an alternative to --entity-id. Reporter env flags (--reporter-git-sha, --reporter-app-version, --reporter-git-ref, --reporter-channel, --reporter-ci-run-id) are accepted on both create and message.
  • neotoma doctor --json adds data.risks[] (icloud_drive, macos_synced_desktop_or_documents, prior_sqlite_repair_artifacts) and data.suggested_safe_data_dir so users can move the SQLite DB off iCloud-synced paths.
  • neotoma storage set-data-dir documents the recommended ~/Library/Application Support/neotoma/data location for macOS.
  • resolveBaseUrl now probes the preferred-env default port before pinning to it; if no API is listening, multi-instance discovery resumes so CLI commands no longer hang against a dead API.

API surface & contracts

  • OpenAPI: 27 new operations (above). ErrorEnvelope.error_code adds ERR_RELATIONSHIP_ENTITY_ID_FORMAT and ERR_CONVERSATION_MESSAGE_ROLE_CONFLICT. entities/query sort_by widens to include submitted_at. AgentDirectoryEntry gains observation_entity_type_counts.
  • MCP tool schemas: submit_issue adds anyOf for reporter env (breaking at the MCP layer for clients that previously omitted both fields). add_issue_message, get_issue_status add anyOf: [entity_id, issue_number]. New tool descriptors for the 17 new MCP tools listed above; updated descriptors document the advisory→private deprecation and the integer type for issue_number.
  • Schema seeding: plan global schema added at boot. issue, conversation, and conversation_message ship with seeded guest-access policies for issue submission (submitter_scoped when neotoma access enable-issues runs).
  • conversation_message: optional reporter_git_sha, reporter_git_ref, reporter_channel, reporter_app_version so reporter env on add_issue_message persists on the thread row.

Behavior changes

  • Issues replace feedback as the primary reporting surface. The legacy feedback intake artifacts (cron and Netlify) are removed; submit_issue / add_issue_message / get_issue_status are the canonical flow, both for in-product agents and external operators. Reporter env is enforced on submit and soft-enforced on public-thread messages.
  • The auth model stays on the hardened v0.11.1 path. Same-host reverse proxies (Caddy, nginx, Cloudflare tunnel, Fly) connect to Node over loopback for public-internet callers. isLocalRequest requires (a) loopback socket, (b) no X-Forwarded-For claim from a non-loopback hop, and (c) either non-production env or explicit NEOTOMA_TRUST_PROD_LOOPBACK=1. The same policy is mirrored in src/services/root_landing/index.ts for landing-mode resolution.
  • Access policy precedence reversed at the source layer. Env vars > SchemaMetadata.guest_access_policy (new canonical source) > config file (now a deprecated fallback that logs a warning on read). loadAccessPolicies / resolveAccessPolicy return the same effective values; loadAccessPolicyEntries and resolveAccessPolicyWithSource expose the winning source so neotoma access list / reset can surface it.
  • MCP profile-aware port-file mode. NEOTOMA_MCP_LOCAL_HTTP_PORT_PROFILE=dev or =prod is now sufficient on its own to enable port-file resolution. This unblocks Cursor's MCP harness, which silently drops "1"-valued env entries while honoring sibling string values like "prod". Legacy callers that pass neither the flag nor a profile fall through to the historical http://127.0.0.1:3080/mcp default.
  • MCP stale session disambiguation. POST /mcp with a stale or unknown session id returns 503 (JSON-RPC -32xx); requests that never initialized return 400. Operators see distinct codes for proxy / restart / load-balancer-induced session loss.
  • Combined-store file sniff. store resolves the file MIME type from the buffer's magic bytes (sniffMimeTypeFromBuffer) before falling back to extension + caller-provided mime_type, so JPEG/PNG/GIF/WebP/PDF/Ogg/FLAC/WAV uploads classify correctly even when the extension is wrong.
  • Guest access tokens carry a TTL. NEOTOMA_GUEST_TOKEN_TTL_SECONDS (default 2,592,000 = 30 days) and revoked_at are persisted on the token observation. Generation errors now throw instead of being logged-and-swallowed.
  • Inbound peer sync webhook is hostname-validated. applyInboundSyncWebhook rejects sender_peer_url values that do not match the configured peer_url after normalization (SENDER_PEER_URL_MISMATCH), and rejects private/loopback hostnames when NEOTOMA_HOSTED_MODE=1 (SENDER_PEER_URL_PRIVATE_HOST).
  • canonicalAauthAuthority consults NEOTOMA_PUBLIC_BASE_URL before falling back to config.apiBase, so AAuth signing on hosted/tunnelled deployments resolves to the public hostname rather than the bound interface.

Agent-facing instruction changes

  • MCP instructions (docs/developer/mcp/instructions.md): display rule overhauled to render 🧠 Neotoma — [<conversation title>](<active Neotoma origin>/inspector/conversations/<entity_id>) headings and per-entity bullets that link the entity_type text to the Inspector record. New sections: [CROSS-INSTANCE SYNC — PEERS], [SUBSTRATE SUBSCRIPTIONS], [ISSUE REPORTING] (renamed from FEEDBACK REPORTING, gated by issues.reporting_mode: proactive / consent / off with a one-time mode-discovery prompt). [STORE RECIPES] documents observation_source (sensor / workflow_state / llm_summary / human / import / sync) and source_peer_id for sync replays.
  • CLI agent instructions (.cursor/rules/neotoma_cli.mdc + canonical copies): same Inspector-linked display rule, plus issue-driven work updates (an issue id in the prompt requires add_issue_message before final reply), GitHub issue URL extraction normalization (github_number + repo identity rule), and explicit pre-release checklist references.
  • Skills: the existing release skill is rewritten to encode the comprehensive-body coverage rule for release supplements; the evaluate site subpage prompts agents to offer a non-destructive Test-safely shadow mode before recommending full implementation.

Tests and validation

  • npx tsc --noEmit
  • npx vitest run tests/contract/ --reporter=verbose
  • npx vitest run tests/cli/cli_command_coverage_guard.test.ts tests/cli/cli_doctor_setup.test.ts tests/cli/config_api_discovery.test.ts tests/cli/processes_command.test.ts --reporter=verbose
  • npx vitest run tests/integration/cross_instance_issues.test.ts tests/integration/tunnel_auth.test.ts tests/integration/root_landing.test.ts --reporter=verbose
  • npx vitest run tests/subscriptions/subscription_guest_auth.test.ts tests/contract/openclaw_plugin.test.ts tests/unit/security_hardening.test.ts tests/unit/client_turn_report.test.ts --reporter=verbose
  • npm run openapi:bc-diff (reports non-breaking; HTTP reporter-env enforcement is application-layer, MCP enforcement is JSON-schema-layer)
  • npm run security:classify-diff (reports sensitive=true)
  • npm run security:lint
  • npm run security:manifest:check
  • npm run test:security:auth-matrix
  • npm run build:server
  • bash scripts/security/deployed_probes.sh --tag v0.12.0 (post-deploy)

Breaking changes

  • MCP submit_issue requires reporter environment. The MCP tool's JSON Schema now declares anyOf: [{ required: [reporter_git_sha] }, { required: [reporter_app_version] }], so MCP clients that previously called submit_issue with only title + body will fail JSON-schema validation locally before the request leaves the client. HTTP callers of POST /issues/submit see the same enforcement at the application layer with 400 ERR_REPORTER_ENVIRONMENT_REQUIRED and details.acceptable_field_groups: [["reporter_git_sha"], ["reporter_app_version"]]. This is a tightening of request-shape validation (per docs/architecture/change_guardrails_rules.mdc rule 14, that is by definition breaking) and is documented at docs/subsystems/issues.md § Reporter provenance.
  • Issues replace feedback at the subsystem boundary. The legacy feedback cron + Netlify intake artifacts are removed. Any consumer that was scripting against the feedback path must move to submit_issue / POST /issues/submit and supply reporter env.
  • src/services/issues/syncIssuesFromGitHub.ts is renamed to sync_issues_from_github.ts. Deep imports against the camelCase filename break; the public service barrel (src/services/issues/index.ts) is unchanged, so most consumers are unaffected.

Migration guide

  • Agents and clients calling submit_issue / POST /issues/submit: add reporter_git_sha (e.g. git rev-parse HEAD) and/or reporter_app_version (e.g. the npm package version) to every call. Daemons and CI runners may also pass reporter_git_ref, reporter_channel, reporter_ci_run_id.
  • Agents calling add_issue_message: when continuing a debugging thread, pass the reporter env that you re-tested against. The server only warns when these are missing on public threads — but every message you store without them loses its connection to the build it describes.
  • Operators running Neotoma behind a reverse proxy in production: set NEOTOMA_BEARER_TOKEN (or enable AAuth) and verify isLocalRequest no longer falsely accepts proxied traffic. If your deployment intentionally needs production loopback to count as local (e.g. an internal admin script), set NEOTOMA_TRUST_PROD_LOOPBACK=1 explicitly.
  • Operators running hosted / tunnelled MCP proxies: set MCP_PROXY_FAIL_CLOSED=1 (or failClosed: true on the proxy options) whenever AAuth signing is required, so unsigned downstream requests never bypass the signer.
  • Operators on Cursor / Claude Code / Codex with profile shims: delete and re-run neotoma mcp config so the regenerated mcp.json picks up the new resolver behaviour (NEOTOMA_MCP_LOCAL_HTTP_PORT_PROFILE alone is now sufficient). The signed and unsigned dev shim scripts both source scripts/lib/neotoma_mcp_resolve_downstream_url.sh, so a fresh checkout immediately gets the cross-profile-leakage fix.
  • Operators with stale dev API instances: reload LaunchAgents once via bash scripts/reload_neotoma_launchagents.sh --kill-zombies. The reloader now recognizes the actual orphan patterns observed on disk (with_branch_ports.js + actions.ts chains, PPID=1 tsx watch zombies, npm exec tsx chains).
  • Operators with iCloud-synced data directories: run neotoma doctor --json and check data.risks. Move NEOTOMA_DATA_DIR to a local-only path (~/Library/Application Support/neotoma/data is the recommended macOS default) with neotoma storage set-data-dir <dir> while Neotoma is stopped.
  • Harness plan files: run neotoma plans capture --all once to backfill any existing .cursor/plans/, .claude/plans/, .codex/plans/, .openclaw/plans/ files into Neotoma. Identity is keyed by (harness, harness_plan_id) so re-running is idempotent.
  • Issue submission access policy: if your operator instance accepts issues from authenticated guests, run neotoma access enable-issues so issue, conversation, and conversation_message are set to submitter_scoped via SchemaMetadata. The deprecated config-file fallback still works but logs a warning on read.

Security hardening

This release builds on the v0.11.1 auth-bypass hotfix and adds the follow-through that makes that fix durable in day-to-day operation: pre-release gates G1–G5, hardened loopback / forwarded-for classification, and explicit operator guidance.

  • isLocalRequest rewrite in src/actions.ts (and the mirrored copy in src/services/root_landing/index.ts): loopback + presence of any non-loopback X-Forwarded-For hop now disqualifies the request from local-dev auth; production loopback is non-local by default. New env knob: NEOTOMA_TRUST_PROD_LOOPBACK=1 (opt-in, off by default).
  • CI security_gates job runs G1 (security:classify-diff with PR base-branch awareness), G2 (security:lint), G3 (security:manifest:check + test:security:auth-matrix) on every pull request. The sandbox weekly-reset workflow runs G5 (scripts/security/deployed_probes.sh) against https://sandbox.neotoma.io and uploads the probe report as an artifact.
  • MCP_PROXY_FAIL_CLOSED operator guidance added to docs/developer/mcp/proxy.md: hosted deployments that require AAuth signing must set this so the proxy refuses to forward unsigned downstream requests if signing or session preflight fails.
  • Guest write rate limiter for /issues/submit, /issues/add_message, /subscribe, /unsubscribe (NEOTOMA_GUEST_WRITE_RATE_LIMIT_PER_MIN, default 30/min), keyed by AAuth thumbprint > hashed guest token > IP to prevent IP-shared accounts from starving each other.
  • OAuth Bearer enforcement on /mcp: unknown / expired tokens return 401 invalid_token with WWW-Authenticate resource-metadata. UUID-shaped tokens used as access_token or Bearer no longer bypass to anonymous; tests under tests/subscriptions/subscription_guest_auth.test.ts and tests/unit/security_hardening.test.ts lock the regression in.
  • Inbound peer sync hardening: applySyncWebhook verifies the HMAC signature against the configured peer secret, then validates that sender_peer_url matches the configured peer_url after normalization, and (in hosted mode) rejects private / loopback / link-local hostnames.
  • Guest access token TTL + revocation: NEOTOMA_GUEST_TOKEN_TTL_SECONDS (default 30 days) and revoked_at are persisted on every token; generation errors throw with descriptive messages.
  • Redaction backstop runs on public submit / public-thread messages so submitter-side redaction bugs do not leak PII; the same regex used by the legacy feedback flow now also skips ISO-date literals so dates like 2026-05-12 are not redacted as phone numbers.
  • SECURITY.md rewrite documents the disclosure flow (private intake → hotfix branch → gates → coordinated release → deployed probes → public advisory), the operator-visible gate summary table, the supported-version range, and the rotation guidance for operators after any advisory.

Per docs/security/threat_model.md, the post-deploy probe report for this tag will land at docs/security/probes/v0.12.0.md.


Commits (v0.11.1v0.12.0)

  • 7e606e5 Merge test/current-branch-build for v0.12.0
  • c9c2e6b Pre-release: include remaining v0.12.0 tests
  • 4ca8f61 Pre-release: include pending changes for v0.12.0
  • 80ae165 fix(launchagents): pin dev/prod to canonical ports and stop chokidar reload from orphaning the API server
  • 654e450 feat: expand issue submission, sync, and site surfaces
  • 33c0840 fix(mcp): treat profile=prod/dev alone as sufficient to enable port-file mode
  • 5c24bbd refactor(mcp): all stdio shim wrappers source the shared env + resolver libs
  • 1c2f98f fix(mcp): harden stdio shim downstream resolver and launchagent reload
  • 629f508 chore: sync local port profile docs and contracts
  • 67e7d5f fix(mcp): support profiled local port files
  • 1f3c487 fix(mcp): clarify unknown HTTP session errors
  • c732589 feat: prepare issue workflow and site build updates

Full compare: v0.11.1...v0.12.0

Breaking Changes

  • MCP `submit_issue` requires reporter environment (anyOf: [reporter_git_sha] or [reporter_app_version]).
  • Legacy feedback subsystem removed; all issue reporting must use new `submit_issue`/`POST /issues/submit` APIs.
  • File rename: `src/services/issues/syncIssuesFromGitHub.ts` → `sync_issues_from_github.ts` (breaks deep imports).

Security Fixes

  • Hardened `isLocalRequest` to reject non‑loopback forwarded hops; production loopback disabled by default (opt‑in via NEOTOMA_TRUST_PROD_LOOPBACK=1).
  • Added CI security gates G1–G5, sandbox probe deployment, and guest write rate limiting.
  • Inbound peer sync webhook validates sender_peer_url and rejects private/loopback hosts in hosted mode.

Weekly OSS security release digest.

The CVE patches and breaking changes that affected production tools this week. One email, every Sunday.

No spam, unsubscribe anytime.

Share this release

Track markmhendrickson/neotoma

Get notified when new releases ship.

Sign up free

About markmhendrickson/neotoma

Deterministic state layer for AI agents. Stores versioned entities (contacts, tasks, transactions, decisions) with immutable observations, full provenance, and schema-first extraction. Local-first SQLite, cross-client memory across Claude, Cursor, ChatGPT, and OpenClaw. Website

All releases →

Related context

Earlier breaking changes

  • v0.12.1 Inspector build prepublish now exits non-zero if inspector submodule is missing, breaking ad-hoc npm pack runs without init.

Beta — feedback welcome: [email protected]