Skip to content

Releases

concealer 1.0.0

Choose a tag to compare

@github-actions github-actions released this 27 Sep 12:10

[1.0.0] — 2026-09-27

First public release. The desktop tool and the iOS/Android mobile app ship on the
same 1.0.0 version line.

Added

  • Mobile app (iOS + Android) — a Capacitor companion app that opens the vault
    offline
    with your master password, works standalone, and syncs bidirectionally
    with the host (POST /api/sync, timestamp last‑write‑wins with deletion tombstones).
    All crypto runs on‑device (age-encryption); only master‑password‑encrypted bundles
    cross the wire. Includes an offline demo mode (no host needed).
  • Automated AI‑agent registration — concealer agent install [--all] detects the AI
    agents on your machine (Claude Code, Cursor, Windsurf, Gemini CLI, Claude Desktop, Codex),
    registers a per‑agent token, writes the MCP config non‑destructively, and installs the
    concealer skill where supported.
  • Auto‑start service — concealer service install|uninstall|status runs the lan
    bridge at login as a per‑user background service (macOS launchd, Linux systemd --user,
    Windows Scheduled Task) so phones can always reach and auto‑discover the vault.
  • LAN discovery — concealer lan advertises the vault over mDNS/Bonjour
    (_concealer._tcp) with a stable vault id, so a phone picks the vault by identity
    rather than by ip/host/port (survives DHCP changes and multiple vaults).
  • Chrome extension — copy secrets per‑field straight from the toolbar; talks only to the
    local 127.0.0.1 server via a built‑in native host (concealer native-host), starts the
    web server on demand and self‑exits after idle.

concealer 0.9.29

Choose a tag to compare

@github-actions github-actions released this 26 Sep 11:50

[0.9.29] — 2026-09-26

Added

  • Demo mode — a friction-free way to explore concealer with sample data, no real vault
    or host needed. concealer demo [port] spins up an isolated throwaway vault (temp dir,
    master password demo1234), seeds six dummy secrets, and serves the web UI with a DEMO
    banner. The mobile app gains a matching "Try the demo — no host needed" button on the
    setup screen that loads the same sample set offline (a DEMO badge shows in the header; sync
    is disabled). All demo values are dummy (DEMO/sk_test_…).

concealer 0.9.28

Choose a tag to compare

@github-actions github-actions released this 25 Sep 12:37

Rollup release: folds in 0.9.25, 0.9.26 and 0.9.27, which were developed between 0.9.24 and this release and never cut their own tag.

[0.9.28] — 2026-09-23

Added

  • Auto-start service — concealer service install|uninstall|status runs the lan bridge
    automatically at login as a per-user background service (macOS launchd, Linux systemd --user,
    Windows Scheduled Task), so phones can always reach and auto-discover the vault with no
    manual concealer lan. Prefers the globally installed concealer so it opens the real vault.
  • LAN discovery — lan now advertises the vault over mDNS/Bonjour (_concealer._tcp, via
    dns-sd/avahi-publish-service, best-effort) with a stable vault id so a phone can pick
    the vault by identity rather than by ip/host/port (handles DHCP changes and multiple vaults
    on one network). New unauthenticated GET /api/serverinfo returns {id, name, version} (no
    secret) for labeling discovered servers and validating a manually entered address. The id is
    minted once and stored in keys/serverinfo.json (git-ignored).
  • Mobile app (iOS + Android) — a new mobile/ Capacitor app that opens the vault
    offline with the master password, imports the whole vault from the host on first
    connect, lets you add/edit/delete secrets on the phone offline, and syncs bidirectionally
    when online. All crypto runs on‑device via the age-encryption JS library (same age
    passphrase format as concealer bundles — no sops/age binary on the phone). At rest the
    phone stores only the master‑password‑encrypted bundle. See mobile/README.md.
  • Bidirectional device sync — foundation for the mobile app. A new authenticated
    POST /api/sync endpoint exchanges an encrypted vault bundle both ways in one call:
    the peer uploads its bundle (encrypted with the master password), the host does a
    last-write-wins merge (newer updated timestamp wins), saves, and returns the full
    merged set (bundle) so the peer converges to the same state. Secret values never cross
    the wire in plaintext — the sync payload uses the same portable bundle encryption as
    export. Requires the master password (human-owner operation), audited as sync.
  • Deletion sync via tombstones — deletes are now soft: a deleted record keeps its id,
    name and scope (all secret values dropped) and is marked with a deleted timestamp so
    the deletion can propagate to other devices. Tombstones never appear in lists, search,
    get, the web UI, MCP enumeration, or human backups/exports — only in device sync.

Fixed

  • rm --name X could delete the wrong record(s) on a vault with two or more live
    records. The scope selector was rebuilt inside a matching loop and the builder mutates
    its argument list, so after the first row the selector became empty and matched every
    remaining record (e.g. rm --name gamma reporting "2 records matched"). The selector is
    now computed once.

[0.9.27] — 2026-09-23

Added

  • concealer agent install [--all|--mcp-only|--skill-only] [ids] — auto-detects the
    AI agents installed on this machine (Claude Code, Cursor, Windsurf, Gemini CLI, Claude
    Desktop, Codex), lets you pick which to set up interactively (or by id / --all), then
    wires concealer's MCP server into each one's config with a fresh per-agent token and
    installs the concealer skill where supported (Claude Code). Replaces the manual
    agent register + claude mcp add two-step. Config merges are non-destructive (existing
    MCP servers preserved) and idempotent. Tokens stay client-side; revoke with
    concealer agent revoke <id>.

[0.9.26] — 2026-09-21

Added

  • concealer lan [lan_port [web_port]] — reach the web UI from a phone (or any
    device) on the same local network. web binds loopback only and rejects
    non-loopback Host/Origin (anti-DNS-rebinding), so it is unreachable from a phone by
    design. lan runs a thin reverse proxy that accepts only private/loopback/link-local
    clients
    , rewrites Host/Origin back to loopback, and forwards to a local web
    instance (auto-started if not already running, using the installed vault). The master
    password stays the only gate; the proxy holds no vault state or key. Phone URL is
    http://<host>.local:<lan_port> (default 8788 → web 8787) — resolvable over Bonjour/mDNS
    with no extra DNS setup. Intended for a trusted LAN (plain HTTP).

[0.9.25] — 2026-09-15

Changed

  • Default CLI unlock-token lifetime is now 1h (was 8h), overridable with
    CONCEALER_TOKEN_TTL (seconds). Shorter default = smaller window if a shell holding the
    token is exposed. unlock now also exports CONCEALER_TOKEN_EXP (expiry epoch) so a
    shell hook can auto-unset the token string once the server-side TTL lapses (the token is
    already rejected after expiry — a child process cannot unset a var in its parent shell).

Security

  • The TUI now always requires the master password. It previously honored a
    CONCEALER_TOKEN in the environment and would open — and reveal secrets — with no
    prompt, so any shell holding an unlock token could browse the vault. The TUI now ignores
    the token and unlocks from the master password on the terminal, matching the web UI's
    per-session unlock. (Token-driven get/list/MCP are unchanged.)

Fixed

  • run_with_secrets / run now inject usable env vars for secrets whose name isn't a
    valid shell identifier.
    A secret named grafana-rpifx previously emitted
    grafana-rpifx_PASSWORD — not referenceable by the child ($grafana-rpifx_PASSWORD
    doesn't parse, compgen -e won't list it), so website/login and other hyphenated
    secrets looked "not injected". Names are now sanitized to valid identifiers
    (grafana-rpifx → GRAFANA_RPIFX_PASSWORD). Colliding names fail loudly instead of
    silently clobbering.

Added

  • CONCEALER_INJECTED — injected into the child env as a comma-separated list of the
    injected identifier names (names only, never values), and shown on an injected: line
    in the run_with_secrets MCP result, so a script/agent can discover what was injected
    without guessing.
  • env_alias on a record — pin a stable env-injection base (e.g. grafana-rpifx →
    GRAFANA) for scripts that reference fixed variable names. Settable via set_secret.
  • Secure Agentic Autofill (fill-without-reveal). A new MCP tool request_web_login
    lets a browser-driving agent sign in to a site with a stored website/login secret
    without the value ever entering the agent/LLM context. The agent only asks; a human
    approves in the concealer web UI (approve once / for this task / deny, with an
    anti-phishing domain match); the concealer browser extension injects the credentials
    into the page's login form and the value flows app → extension → DOM only. Every step is
    audited (web_autofill_request/approve/deny/fill/exposed) and the agent receives only a
    status string. The extension gains a background service worker + content script (form
    detection, native-setter fill with input/change events, and a post-fill DOM exposure
    scan). Loopback endpoints /api/autofill/{request,pending,jobs,approve,deny,claim,result,status}
    carry the handshake; the MCP process reaches the web server on CONCEALER_WEB_PORT
    (default 8787).

concealer 0.9.24

Choose a tag to compare

@github-actions github-actions released this 08 Sep 21:14

[0.9.24] — 2026-09-09

Fixed

  • Web UI showed a blank “webui.html not found” page after an in-place upgrade. A concealer web
    server started before the upgrade keeps running from its versioned install dir, which the package
    manager deletes — so it can no longer read its own webui.html. The server now falls back to the
    current install’s copy (resolved via the concealer/cer on PATH) and, if truly gone, shows an
    actionable “quit and run cer web again” message instead of a blank page. (Existing stale servers
    from before this version must still be quit once — this heals the next upgrade.)

Changed

  • Chrome extension no longer stalls on open with a large vault. The popup rendered a DOM row for
    every secret at once, which felt like a hang. It now renders the first 50 matches and shows
    “showing 50 of N — type to filter”; the search box still filters the whole vault instantly.

concealer 0.9.23

Choose a tag to compare

@github-actions github-actions released this 08 Sep 19:13

[0.9.23] — 2026-09-08

Fixed

  • Settings page lost the auto-backup config (and HIBP key status) on every refresh.
    The i18n pass set textContent on every [data-i18n] element, which wiped the dynamic
    <span> children living inside labelled nodes (#hibp_set, #bk_pw_set, #pol_badge).
    With #hibp_set gone, loadSettings() threw before it reached loadBackup(), so the
    backup fields were never populated and looked empty/unsaved even though the config was on
    disk. The translation now updates only the leading text node when an element has child
    elements, preserving those spans.

Added

  • The Settings page footer now shows the brand + version + GitHub link, matching the login screen.
  • Clicking the concealer logo/name (top-left) returns to the Secrets home view.
  • Landing page: the “concealer” wordmark is marked translate="no" so a browser’s built-in
    page-translation leaves the brand intact instead of mangling it (e.g. into “gizlemeker” in Turkish).
    The brand stays “concealer” in every language.

Fixed

  • Chrome extension stuck on the old version / "webui.html not found" after an upgrade.
    A concealer web server started before the upgrade keeps running from the now-deleted
    install directory, so it can no longer find its own webui.html. The extension's native
    host saw the port already open and blindly reused that dead server. The native host now
    checks the running server's version (new version field in /api/session); if it does
    not match, it asks the old server to stop (new /api/shutdown) and launches the current
    one. Upgrades from this version on heal automatically; a server already running from a
    pre-0.9.22 install must be quit once (or reboot) — the popup now says so instead of
    showing a blank page.

Added

  • The extension shows a version-mismatch banner when its version differs from the running
    app, with a reminder to update concealer (brew/scoop/pip) and reload the extension.
  • concealer version --check — opt-in, on-demand check for a newer release. concealer makes
    no network calls unless you run this; there is no automatic/background update polling.

concealer 0.9.22

Choose a tag to compare

@github-actions github-actions released this 08 Sep 15:16

[0.9.22] — 2026-09-08

Fixed

  • Chrome extension stuck on the old version / "webui.html not found" after an upgrade.
    A concealer web server started before the upgrade keeps running from the now-deleted
    install directory, so it can no longer find its own webui.html. The extension's native
    host saw the port already open and blindly reused that dead server. The native host now
    checks the running server's version (new version field in /api/session); if it does
    not match, it asks the old server to stop (new /api/shutdown) and launches the current
    one. Upgrades from this version on heal automatically; a server already running from a
    pre-0.9.22 install must be quit once (or reboot) — the popup now says so instead of
    showing a blank page.

Added

  • The extension shows a version-mismatch banner when its version differs from the running
    app, with a reminder to update concealer (brew/scoop/pip) and reload the extension.
  • concealer version --check — opt-in, on-demand check for a newer release. concealer makes
    no network calls unless you run this; there is no automatic/background update polling.

concealer 0.9.21

Choose a tag to compare

@github-actions github-actions released this 08 Sep 09:37

[0.9.21] — 2026-09-08

Fixed

  • Chrome extension stopped working after upgrading concealer ("Couldn't reach the
    concealer server (Native host has exited.)"). The registered native-host launcher
    recorded the resolved script path, which package managers place under a versioned
    directory (…/Cellar/<ver>/libexec/concealer, scoop's app/<ver>); the next upgrade
    deletes that directory, so the helper died the instant Chrome launched it — and with
    the helper dead the popup never reached the master-password step either. The launcher
    now records the stable command on PATH (which package managers re-point on upgrade)
    and re-resolves at run time if that is gone.
  • concealer web now repairs a launcher left dangling by an earlier upgrade, so an
    affected install heals itself on the next run.
  • The extension popup shows the setup card (with the cer chrome-extension command)
    for any native-host failure, not just an unregistered host — previously a broken
    helper produced only a raw Chrome error with no hint of the fix.

concealer 0.9.20

Choose a tag to compare

@github-actions github-actions released this 08 Sep 08:16

[0.9.20] — 2026-09-08

Fixed

  • Import in the web UI did nothing when clicked. The file was base64-encoded
    with btoa(String.fromCharCode(...new Uint8Array(buf))), which blows the JS call
    stack for any file over ~100 KB — a real .cerbak is far bigger, so the reader
    callback threw before the password prompt could open and the click looked inert.
    Encoding is now chunked (b64enc()), and read/encode failures surface a toast
    instead of dying silently.
  • Import now asks for "password of the file (backup / export password)" rather
    than "master password" — a .cerbak is opened with the password it was written
    with, which is usually not this machine's master password.
  • concealer init could overwrite a hardened vault. The "already set up" guard only
    checked keys/age-key.txt, which hardening deliberately removes — so on a hardened vault
    init sailed past the guard and generated a fresh keypair, orphaning every secret. It now
    also checks the encrypted key backup.
  • Login background: redaction bars no longer draw a line through themselves.
    Each redacted fragment painted a solid bar (.rx::after) and a second
    strike-through rule (.rx::before, rxstrike) across its middle, so a covered
    secret read as struck-out rather than concealed. The strike layer is gone — a
    redaction is now just the bar. Same fix in the docs landing page, which carries
    a copy of the animation.

Changed

  • No user-facing surface names the underlying crypto tools any more. The CLI banner,
    --help header, TUI splash, init/harden output, dependency-missing message, web UI
    warnings and every error string now say "vault key" / "encryption engine" instead of
    naming SOPS or age. Subprocess stderr is passed through a new scrub() at each boundary
    that reaches a user (CLI exits, web /api/* errors, MCP tool errors, TUI status line), so
    a leaking tool error can't reintroduce the names. Implementation is unchanged — encryption
    is still delegated, and internal paths, env vars and argv keep their real names.
  • One extension everywhere: .cerbak. Export no longer writes .age — the web
    and CLI both produce concealer-export-YYYY-MM-DD.cerbak, in the same opaque
    container as backups. Buttons, hints, file picker and --help follow. Existing
    .age / .cer files still import unchanged (the reader auto-detects the format).

concealer 0.9.19

Choose a tag to compare

@github-actions github-actions released this 03 Sep 06:21

[0.9.19] — 2026-09-02

Changed

  • Secrets table columns no longer auto-flood. Custom-field columns now use an
    allowlist (sc_col_shown, default = base columns only), so newly-appearing
    field columns stay hidden until the user opts them in — instead of dozens of
    columns sliding in on every load.
  • Columns picker moved from the table header ⚙ into a toolbar button next to
    New Secret / Scan folder / Generate. The modal gained Select all / Default /
    None buttons, per-type template presets (pick a type → show its standard
    fields), and a live search box.
  • Risks → Exposure (online leak check) now requires narrowing: at least one
    filter (scope/collection/tag) must be selected before any record list is shown,
    and Scan leaks stays disabled until at least one record is picked — no more
    implicit "none picked = scan everything".
  • Secrets table is now a proper frozen-column grid. Columns size to their
    content instead of crushing into the viewport (which word-wrapped headers/values
    into an ugly stack). Horizontal scrolling is contained inside the table — the page
    no longer shifts sideways — with the NAME column frozen on the left, ACTIONS frozen
    on the right, and everything between scrolling. NAME is never auto-truncated.
  • Audit Logs columns picker moved from the table-header ⚙ into a toolbar button
    (next to Verify chain / CSV / JSON), matching the Secrets page. The audit grid now
    auto-sizes columns to their content (was fixed-width), so TIME no longer word-wraps.

Security

  • Secret field values are fully masked (••••••••) in the secrets list, instead
    of the partial sk-D…xy mask that leaked the first/last characters. The partial
    mask still appears in detail/risk views where identifying the value is the point.

concealer 0.9.18

Choose a tag to compare

@github-actions github-actions released this 02 Sep 10:44
b44f186

[0.9.18] — 2026-09-01

Security

  • Hardened the web UI against XSS (OWASP audit F1/F5). A secret's url field is now passed through a
    scheme allow-list (safeUrl → only http/https/mailto) before it can become a clickable link, so a
    stored javascript:/data: URI (e.g. planted via the MCP set_secret url arg) can no longer execute
    when the owner clicks it. esc() now also escapes ' and backtick, and the copy-value button builds its
    handler argument via JSON.stringify instead of hand-rolled quote-escaping (fixes a backslash/newline
    breakout). rel="noopener" added to the remaining target="_blank" links.
  • Content-Security-Policy + security response headers (F2/F3). The web server now sends Content-Security-Policy
    (default-src 'none', connect-src 'self', frame-ancestors 'none', …; script-src/style-src keep
    'unsafe-inline' until the inline handlers are refactored), plus X-Content-Type-Options: nosniff,
    X-Frame-Options: DENY, Referrer-Policy: no-referrer, and Cache-Control: no-store on every response.
    A matching CSP <meta> is embedded in webui.html as a fallback.
  • Anti-DNS-rebinding guard (F4). The localhost API now rejects requests whose Host is not
    127.0.0.1/localhost and whose Origin (when present) is neither loopback nor a chrome-extension://
    page, returning 403. Blocks a malicious web page / DNS-rebinding attack from driving the API in the
    victim's browser.
  • Stronger master-password KDF (F6). The master.json verifier now uses scrypt N=2^17 (was 2^14),
    matching current OWASP guidance; maxmem scales with the cost factor. High-entropy token/recovery hashes
    keep the cheaper factor. Existing vaults are lazy-upgraded to the stronger N on the next successful
    master-password check — no user action needed.