Changelog

What shipped, including what we got wrong.

Every release, in order. When Brainlyy gets something wrong, the correction goes here rather than quietly into a commit message.

Unreleased

  1. FIXEDthe important one

    A note could call itself verified. The dashboard decided a note was VERIFIED by checking whether its text contained the word "VERIFIED", and drew a green badge for it. Nothing verified anything, in the one project whose premise is catching claims that carry no check.

    The status is now computed by an evaluator and never by the note. A claim declares named evidence; the names resolve only to checks in config, because a note is untrusted input. Three states: VERIFIED (every check passed, recently), UNVERIFIED (checkable, and something failed or expired), and ASSERTED (not machine-checkable, which is legitimate, and where most notes live). A status: field in frontmatter is ignored and flagged as self-certification.

    This demotes most existing notes, which is the correct outcome. The badges were wrong.

  2. ADDED

    brainlyy verify, which re-checks every claim, records what was observed to an append-only log, and reports how long the longest-lived false claim read VERIFIED before a run caught it. --strict exits 1 for CI.

  3. ADDED

    docs/claims.md.

  4. ADDED

    A dashboard. brainlyy ui serves a local web UI: tier and capabilities, note search, a vault browser where provenance is the primary visual dimension, and a code-graph view. Light and dark, phone-first. Vanilla HTML/CSS/JS served by the same Python process, with no Node, no bundler and no build step. Fonts are bundled rather than fetched, so it renders identically with no internet.

  5. ADDED

    A sign-in page. brainlyy passphrase sets a local passphrase; the dashboard signs in at /login. scrypt (n=2¹⁵) with a random salt and the parameters stored beside the hash so they can be raised later; the passphrase itself is never written. Sessions are signed rather than stored, and the signature is verified before the payload reaches a JSON parser. Five failures locks an address out for fifteen minutes. No third-party identity provider, no account, and it works offline. For a team, put an OIDC reverse proxy in front instead, which the security page explains.

  6. ADDED

    Security headers on every response: nosniff, no-referrer, X-Frame-Options: DENY, and a CSP with no unsafe-inline. The pre-paint theme script is allowed by a sha256 computed from the exact bytes served, so the hash cannot drift from the file.

  7. ADDED

    Browser sessions. /?k=<token> is exchanged for an HttpOnly, SameSite=Strict cookie and redirected, so the token leaves the URL bar. Neither cookie ever authenticates /mcp, because the browser attaches them automatically, so anything they open is open to any page the browser is on, and the tools must never be in that set.

  8. ADDED

    ui.bind (loopback by default, mesh to opt in) and ui.remote_writes (off by default), judged on the address the connection arrived on.

  9. ADDED

    A JSON API: /api/status, /api/search, /api/notes, /api/notes/{path}, /api/agents, /api/graph.

  10. ADDED

    Write actions, loopback-gated. POST /api/agent/run, /api/rate and /api/post are proxied to the loopback write server with its own token, so everything past that hop is enforced exactly as it is for a local caller. The UI decides only whether to forward, judged on the address the connection arrived on. Every refusal is distinguishable on purpose: 401 no credential, 403 wrong door, 400 bad body, 503 write server not running, 502 it refused. /api/status.writes_enabled is computed by the same function that gates the endpoints, so the dashboard cannot render a control the server would refuse.

  11. ADDED

    A code-graph explorer. Canvas rendering with the layout settled across animation frames so the page never blocks, pan and zoom, a floating toolbar, counted communities and kind: / repo: / path: filter chips. Totals are always exact while only the 450 most-connected nodes are drawn. A dashboard that reported the size of its own sample would be lying in the most plausible way. Symbols are namespaced per repository, because two repos that both define main are two symbols, and merging them would invent call edges that do not exist.

  12. ADDED

    brainlyy docs --build renders docs/*.md into one self-contained page in the same design system, with the fonts inlined. The markdown ships in the wheel, so it works on a machine that has never seen the repository.

  13. ADDED

    LICENSE, MIT, matching what pyproject.toml and the README already claimed. The repository was not actually licensed before this.

  14. ADDED

    Continuous integration: the property suite runs on push across Linux, macOS and Windows on Python 3.10 to 3.13, plus a clean-machine brainlyy init.

  15. ADDED

    Documentation set: quickstart, concepts, install, CLI reference, security, glossary and FAQ.

  16. ADDED

    CONTRIBUTING.md.

  17. FIXED

    Config discovery preferred the working directory over an explicitly set BRAINLYY_HOME, so cd silently changed which instance answered: standing in a directory that happened to hold an unrelated config.yaml loaded it, and the tool then reported confidently about the wrong vault. An explicitly named instance now wins. Found by running the property suite from an installed package, where the instance directory is also the working directory.

0.1.02026-09-03

  1. ADDED

    Installable package. brainlyy init / doctor / serve / search.

  2. ADDED

    Two MCP servers: a read server bound to a mesh address, and a write server that exits at startup if configured to bind anything but loopback.

  3. ADDED

    Capability detection with tiers 0 to 4. Nothing is required; each dependency found turns on more.

  4. ADDED

    Tier 0 note search: lexical ranking over a markdown vault, no vector store.

  5. ADDED

    Knowledge and agent (SOUL) templates, rendered by init.

  6. ADDED

    A property suite of 66 assertions covering bind addresses, the loopback guard, allowlist refusals, and that no deployment-specific value travels with the package. It ships inside the wheel, so an installed instance verifies itself with the same assertions.

  7. FIXED

    register() resolved claude with shutil.which() and then invoked the bare name. On Windows those are different lookups, because CreateProcess appends .exe, so it verified one executable and ran another.

  8. FIXED

    _detect_mesh_ip() tested startswith("100."), which matches public address space, and excluded the CGNAT base address by string prefix rather than by range.

  9. FIXED

    _fill() silently passed through a config key that matched no template line, handing the user the example's placeholder values as their own answers.