Skip to content

chore: release v0.1.0 - #46

Merged
frol merged 1 commit into
mainfrom
release-plz-2026-02-12T11-34-45Z
Feb 12, 2026
Merged

frol merged 1 commit into
mainfrom
release-plz-2026-02-12T11-34-45Z

Conversation

@github-actions

@github-actions github-actions Bot commented Feb 12, 2026 •

Copy link
Copy Markdown
Contributor

🤖 New release

  • ironclaw: 0.1.0
Changelog

0.1.0 - 2026-02-12

Added

  • Add multi-provider LLM support via rig-core adapter (#36)
  • Sandbox jobs (#4)
  • Add Google Suite & Telegram WASM tools (#9)
  • Improve CLI (#5)

Fixed

  • resolve runtime panic in Linux keychain integration (#32)

Other

  • Skip release-plz on forks
  • Upgraded release-plz CD pipeline
  • Added CI/CD and release pipelines (#45)
  • DM pairing + Telegram channel improvements (#17)
  • Fixes build, adds missing sse event and correct command (#11)
  • Codex/feature parity pr hook (#6)
  • Add WebSocket gateway and control plane (#8)
  • select bundled Telegram channel and auto-install (#3)
  • Adding skills for reusable work
  • Fix MCP tool calls, approval loop, shutdown, and improve web UI
  • Add auth mode, fix MCP token handling, and parallelize startup loading
  • Merge remote-tracking branch 'origin/main' into ui
  • Adding web UI
  • Rename setup CLI command to onboard for compatibility
  • Add in-chat extension discovery, auth, and activation system
  • Add Telegram typing indicator via WIT on-status callback
  • Add proactivity features: memory CLI, session pruning, self-repair notifications, slash commands, status diagnostics, context warnings
  • Add hosted MCP server support with OAuth 2.1 and token refresh
  • Add interactive setup wizard and persistent settings
  • Rebrand to IronClaw with security-first mission
  • Fix build_software tool stuck in planning mode loop
  • Enable sandbox by default
  • Fix Telegram Markdown formatting and clarify tool/memory distinctions
  • Simplify Telegram channel config with host-injected tunnel/webhook settings
  • Apply Telegram channel learnings to WhatsApp implementation
  • Merge remote-tracking branch 'origin/main'
  • Docker file for sandbox
  • Replace hardcoded intent patterns with job tools
  • Fix router test to match intentional job creation patterns
  • Add Docker execution sandbox for secure shell command isolation
  • Move setup wizard credentials to database storage
  • Add interactive setup wizard for first-run configuration
  • Add Telegram Bot API channel as WASM module
  • Add OpenClaw feature parity tracking matrix
  • Add Chat Completions API support and expand REPL debugging
  • Implementing channels to be handled in wasm
  • Support non interactive mode and model selection
  • Implement tool approval, fix tool definition refresh, and wire embeddings
  • Tool use
  • Wiring more
  • Add heartbeat integration, planning phase, and auto-repair
  • Login flow
  • Extend support for session management
  • Adding builder capability
  • Load tools at launch
  • Fix multiline message rendering in TUI
  • Parse NEAR AI alternative response format with output field
  • Handle NEAR AI plain text responses
  • Disable mouse capture to allow text selection in TUI
  • Add verbose logging to debug empty NEAR AI responses
  • Improve NEAR AI response parsing for varying response formats
  • Show status/thinking messages in chat window, debug empty responses
  • Add timeout and logging to NEAR AI provider
  • Add status updates to show agent thinking/processing state
  • Add CLI subcommands for WASM tool management
  • Fix TUI shutdown: send /shutdown message and handle in agent loop
  • Remove SimpleCliChannel, add Ctrl+D twice quit, redirect logs to TUI
  • Fix TuiChannel integration and enable in main.rs
  • Integrate Codex patterns: task scheduler, TUI, sessions, compaction
  • Adding LICENSE
  • Add README with IronClaw branding
  • Add WASM sandbox secure API extension
  • Wire database Store into agent loop
  • Implementing WASM runtime
  • Add workspace integration tests
  • Compact memory_tree output format
  • Replace memory_list with memory_tree tool
  • Simplify workspace to path-based storage, remove legacy code
  • Add NEAR AI chat-api as default LLM provider
  • Add CLAUDE.md project documentation
  • Add workspace and memory system (OpenClaw-inspired)
  • Initial implementation of the agent framework


This PR was generated with release-plz.

@github-actions
github-actions Bot force-pushed the release-plz-2026-02-12T11-34-45Z branch from 15822fc to 0d02c31 Compare February 12, 2026 11:36
@frol

frol commented Feb 12, 2026 •

Copy link
Copy Markdown
Collaborator

@ilblackdragon Before merging, finalize the secrets configuration: #45 (comment).

Once this PR is merged:

  1. the package will be published to crates io,
  2. git tag will be created,
  3. the binary packages for Windows, Linux, MacOS (amd64 and arm64 platforms) will be built,
  4. GitHub release will be created (with the pre-built binaries and installation command available),
  5. and npm installer package will be published.

@frol
frol merged commit 1cf08a4 into main Feb 12, 2026
serrrfirat pushed a commit to serrrfirat/ironclaw that referenced this pull request Feb 16, 2026
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
jaswinder6991 pushed a commit to jaswinder6991/ironclaw that referenced this pull request Feb 26, 2026
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
bkutasi pushed a commit to bkutasi/ironclaw that referenced this pull request Mar 28, 2026
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
zmanian added a commit that referenced this pull request May 5, 2026
Closes the remaining libSQL/Postgres/JSONL gaps surfaced in the latest
serrrfirat review pass on PR #3171.

libSQL production gate (#34, #36, #41):
- Reject `:memory:` for Production via the InMemory error variant.
- Case-insensitive scheme matching for `HTTP://` / `HTTPS://` /
  `LibSQL://`, so a mixed-case URL no longer falls through to
  Builder::new_local and silently creates a node-local SQLite path.
- Reject bare hostname-like values (e.g. `db.example.com`) for
  Production via a new ProductionLibsqlAmbiguousTarget error; LocalDev
  still accepts ergonomic forms like `events.db`.

libSQL backend hardening (#45, #49, #50):
- Skip `create_dir_all("")` for `events.db` in cwd.
- Enable `journal_mode=WAL` + `synchronous=NORMAL` once at build time
  for file-backed local stores so a long replay reader can no longer
  block writer commits past `busy_timeout`.
- Mirror the v1 retry pattern (`src/db/libsql/mod.rs::connect`): three
  attempts with exponential backoff so concurrent transient
  "unable to open database file" errors do not surface as durable-log
  failures.

Postgres backend hardening (#46):
- Honor `sslmode=require` for loopback configs so a TLS-only local
  Postgres / loopback proxy is accepted instead of forced to NoTls.

JSONL hardening (#38, #40, #42, #43, #44):
- Make JSONL constructors crate-private so production composition
  cannot bypass the single-node-durable acceptance gate.
- Hash path components (SHA-256 / 16-hex-char prefix + 32-byte URL-
  encoded hint) so case-distinct IDs (`Alice` vs `alice`) cannot
  collide on case-insensitive filesystems and 256-byte scope IDs no
  longer overflow the 255-byte filename limit.
- Snapshot file length before write/flush/sync; truncate back on any
  error so a partial write never leaves a torn JSON tail that wedges
  every subsequent append.
- Create directories with `0o700` and stream files with `0o600` on
  Unix so durable history is not world-readable under the typical
  `umask 022`.

SQL replay correctness (#37):
- After fetching filtered rows, run a small unfiltered COUNT over the
  scanned cursor window in both libSQL and Postgres backends; mismatch
  surfaces a missing entry row as `EventError::ReplayGap`. JSONL
  already detects this via line-by-line cursor sequencing.

Regression tests:
- libSQL/Postgres production gate (case-insensitive scheme, in-memory,
  bare hostname).
- Hashed-path-component case distinctness and length boundedness.
- Atomic JSONL append: failed serialise leaves file at pre-append
  length and the next append still advances cursor cleanly.
- Unix `0o700` JSONL root permissions.
- libpq quoted single-quote socket path is local; whitespace-around-
  `=` keyword strings classify remote correctly.
- libSQL replay surfaces a deleted entry row as `ReplayGap`.

Deferred with explicit acknowledgement:
- #39 (`ReadScope.invocation_id`) — module-doc note; needs a
  cross-crate change to `ironclaw_events` plus every replay caller.
- #48 (replay holds writer-blocking locks) — inline note at the JSONL
  read path; needs a stream-bytes-snapshot redesign coordinated with
  the durable-log contract.

Test plan
- cargo test -p ironclaw_reborn_event_store
- cargo test -p ironclaw_reborn_event_store --features "libsql postgres"
- cargo test -p ironclaw_host_runtime
- cargo test -p ironclaw_events
- cargo test -p ironclaw_architecture reborn_crate_dependency_boundaries_hold
- cargo clippy -p ironclaw_reborn_event_store --all-targets -- -D warnings
- cargo clippy -p ironclaw_reborn_event_store --features "libsql postgres" --all-targets -- -D warnings
- cargo clippy -p ironclaw_reborn_event_store --no-default-features --all-targets -- -D warnings
- cargo clippy -p ironclaw_host_runtime --all-targets -- -D warnings
- cargo fmt --all -- --check

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
serrrfirat added a commit that referenced this pull request May 6, 2026
* Add Reborn event store backends

* Address Reborn event store review feedback

* Address Reborn event store review feedback (round 2)

Postgres TLS / fail-closed (comment 3178548634): the Postgres event-store
client now uses tokio-postgres-rustls for any non-loopback URL, mirroring
src/db/tls.rs (native certs with webpki-roots fallback). Local sockets and
loopback hosts continue to use NoTls; unparseable URLs with a scheme are
treated as remote so a typo cannot silently downgrade to plaintext.

next_cursor advancement (comment 3178548646): JSONL, libSQL, and Postgres
read paths now track the highest scanned cursor independently from the last
matched cursor, and return max(last_matched, last_scanned). Without this,
a matched record followed by a filtered-out record would leave next_cursor
pinned to the matched cursor and the filtered record would be rescanned on
every subsequent replay.

JSONL bounded replay (comment 3178548670): replays now stream the JSONL
file line-by-line via BufReader, decoding only the cursor envelope until
a line crosses `after`, and stop as soon as `limit` matches are collected.
A `limit = 1` request against a multi-gigabyte stream no longer pays
full-file allocation or full-stream parse latency.

JSONL cross-process locking (comment 3178548701): JSONL appends now take
an OS-level exclusive advisory lock (std::fs::File::lock) for the entire
read-tail-cursor + write window. Two IronClaw processes pointing at the
same JSONL root will block on this lock and emit monotonically-sequenced
cursors instead of corrupting the stream with duplicates. Readers take a
shared lock to avoid observing partially-written tail lines.

New deps on the event-store crate (postgres feature only):
tokio-postgres-rustls, rustls, rustls-native-certs, webpki-roots, url.
All four were already used elsewhere in the workspace; no new external
crates are introduced. File locking uses stdlib (Rust 1.89+ stable).

Regression tests added:
- jsonl_replay_advances_next_cursor_past_trailing_filtered_records
- jsonl_concurrent_appenders_emit_monotonic_cursors_through_file_lock
- jsonl_bounded_replay_does_not_parse_the_whole_file
- postgres_store::tests::{local_postgres_urls_are_recognised,
  remote_postgres_urls_require_tls,
  unparseable_postgres_url_with_scheme_falls_closed_to_remote}

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(reborn_event_store): close TLS bypass for libpq keyword strings and http:// libsql in production

Two High-severity findings from PR #3171 review:

1. is_local_postgres_url() previously returned true for any string without
   "://", so libpq keyword form like "host=db.example.com user=..." took
   the NoTls branch and connected over plaintext. Walk the keyword list,
   look at the actual host, and only treat localhost / socket paths as
   local. Tests cover remote keyword strings, socket paths, and missing
   host=.

2. Production profile previously accepted http:// libSQL URLs and forwarded
   the auth token in cleartext. Reject http:// for RebornProfile::Production
   with a typed error before the build call. LocalDev / Test still accept
   http:// for running against local sqld instances. Tests cover both.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(reborn_event_store): use deadpool-postgres pool for transparent reconnection

Replaces the single Arc<Client> in PostgresStore with a deadpool_postgres::Pool.
The previous design built one tokio_postgres::Client at startup; if the
underlying connection task exited (idle timeout, server restart, failover,
transient network drop), all subsequent append/read operations would fail
forever because nothing rebuilt the connection.

deadpool's Manager replaces broken Clients on the next pool.get() call, so
each call site sees a live connection without per-site reconnect logic.
This mirrors v1's connection-management story (src/db/postgres.rs,
src/db/tls.rs both use deadpool-postgres) and avoids two divergent
reconnection paths in one binary.

Addresses serrrfirat's Medium-severity finding on PR #3171.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* chore(tests): fix post-merge clippy warning

* fix(reborn_event_store): close TLS-mode + libpq-config + JSONL fsync gaps

Three follow-ups from PR #3171 round-3 review:

1. CRITICAL — remote Postgres TLS not actually enforced. Passing a rustls
   connector is not enough on its own: tokio-postgres only consults the
   connector when Config::ssl_mode is Prefer or Require, and `sslmode=disable`
   in the connection string returns a plaintext stream before TLS is
   attempted. Add `enforce_remote_ssl_mode`: reject Disable for non-local
   configs, and force Prefer (the default) up to Require so the server
   cannot decline TLS without failing the connection.

2. HIGH — local/remote detector missed valid libpq forms. Re-parsing the
   raw connection string failed on `hostaddr=10.0.0.5` (numeric-IP keyword,
   no `host=` entry), `postgresql:///db?host=db.example.com` (URL with
   empty authority + host in query), and `host=/var/run/postgresql,
   db.example.com` (mixed socket/TCP list). Replace with
   `is_local_postgres_config` that walks the parsed `Config::get_hosts()`
   and `Config::get_hostaddrs()` so all libpq normalisations land in the
   same code path.

3. MEDIUM — JSONL append fsynced file contents but not the parent
   directory entry. On POSIX the new file's directory entry must also be
   fsynced for crash durability; without it the first append can vanish
   after a power loss even though `append()` returned success. Detect
   first-create via `path.exists()` pre-open and fsync the parent dir
   after `sync_data()`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(reborn_event_store): address PR #3171 round-3 review findings

Closes the remaining libSQL/Postgres/JSONL gaps surfaced in the latest
serrrfirat review pass on PR #3171.

libSQL production gate (#34, #36, #41):
- Reject `:memory:` for Production via the InMemory error variant.
- Case-insensitive scheme matching for `HTTP://` / `HTTPS://` /
  `LibSQL://`, so a mixed-case URL no longer falls through to
  Builder::new_local and silently creates a node-local SQLite path.
- Reject bare hostname-like values (e.g. `db.example.com`) for
  Production via a new ProductionLibsqlAmbiguousTarget error; LocalDev
  still accepts ergonomic forms like `events.db`.

libSQL backend hardening (#45, #49, #50):
- Skip `create_dir_all("")` for `events.db` in cwd.
- Enable `journal_mode=WAL` + `synchronous=NORMAL` once at build time
  for file-backed local stores so a long replay reader can no longer
  block writer commits past `busy_timeout`.
- Mirror the v1 retry pattern (`src/db/libsql/mod.rs::connect`): three
  attempts with exponential backoff so concurrent transient
  "unable to open database file" errors do not surface as durable-log
  failures.

Postgres backend hardening (#46):
- Honor `sslmode=require` for loopback configs so a TLS-only local
  Postgres / loopback proxy is accepted instead of forced to NoTls.

JSONL hardening (#38, #40, #42, #43, #44):
- Make JSONL constructors crate-private so production composition
  cannot bypass the single-node-durable acceptance gate.
- Hash path components (SHA-256 / 16-hex-char prefix + 32-byte URL-
  encoded hint) so case-distinct IDs (`Alice` vs `alice`) cannot
  collide on case-insensitive filesystems and 256-byte scope IDs no
  longer overflow the 255-byte filename limit.
- Snapshot file length before write/flush/sync; truncate back on any
  error so a partial write never leaves a torn JSON tail that wedges
  every subsequent append.
- Create directories with `0o700` and stream files with `0o600` on
  Unix so durable history is not world-readable under the typical
  `umask 022`.

SQL replay correctness (#37):
- After fetching filtered rows, run a small unfiltered COUNT over the
  scanned cursor window in both libSQL and Postgres backends; mismatch
  surfaces a missing entry row as `EventError::ReplayGap`. JSONL
  already detects this via line-by-line cursor sequencing.

Regression tests:
- libSQL/Postgres production gate (case-insensitive scheme, in-memory,
  bare hostname).
- Hashed-path-component case distinctness and length boundedness.
- Atomic JSONL append: failed serialise leaves file at pre-append
  length and the next append still advances cursor cleanly.
- Unix `0o700` JSONL root permissions.
- libpq quoted single-quote socket path is local; whitespace-around-
  `=` keyword strings classify remote correctly.
- libSQL replay surfaces a deleted entry row as `ReplayGap`.

Deferred with explicit acknowledgement:
- #39 (`ReadScope.invocation_id`) — module-doc note; needs a
  cross-crate change to `ironclaw_events` plus every replay caller.
- #48 (replay holds writer-blocking locks) — inline note at the JSONL
  read path; needs a stream-bytes-snapshot redesign coordinated with
  the durable-log contract.

Test plan
- cargo test -p ironclaw_reborn_event_store
- cargo test -p ironclaw_reborn_event_store --features "libsql postgres"
- cargo test -p ironclaw_host_runtime
- cargo test -p ironclaw_events
- cargo test -p ironclaw_architecture reborn_crate_dependency_boundaries_hold
- cargo clippy -p ironclaw_reborn_event_store --all-targets -- -D warnings
- cargo clippy -p ironclaw_reborn_event_store --features "libsql postgres" --all-targets -- -D warnings
- cargo clippy -p ironclaw_reborn_event_store --no-default-features --all-targets -- -D warnings
- cargo clippy -p ironclaw_host_runtime --all-targets -- -D warnings
- cargo fmt --all -- --check

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: serrrfirat <f@nuff.tech>
theredspoon referenced this pull request in theredspoon/ironclaw Jun 21, 2026
* Add Reborn event store backends

* Address Reborn event store review feedback

* Address Reborn event store review feedback (round 2)

Postgres TLS / fail-closed (comment 3178548634): the Postgres event-store
client now uses tokio-postgres-rustls for any non-loopback URL, mirroring
src/db/tls.rs (native certs with webpki-roots fallback). Local sockets and
loopback hosts continue to use NoTls; unparseable URLs with a scheme are
treated as remote so a typo cannot silently downgrade to plaintext.

next_cursor advancement (comment 3178548646): JSONL, libSQL, and Postgres
read paths now track the highest scanned cursor independently from the last
matched cursor, and return max(last_matched, last_scanned). Without this,
a matched record followed by a filtered-out record would leave next_cursor
pinned to the matched cursor and the filtered record would be rescanned on
every subsequent replay.

JSONL bounded replay (comment 3178548670): replays now stream the JSONL
file line-by-line via BufReader, decoding only the cursor envelope until
a line crosses `after`, and stop as soon as `limit` matches are collected.
A `limit = 1` request against a multi-gigabyte stream no longer pays
full-file allocation or full-stream parse latency.

JSONL cross-process locking (comment 3178548701): JSONL appends now take
an OS-level exclusive advisory lock (std::fs::File::lock) for the entire
read-tail-cursor + write window. Two IronClaw processes pointing at the
same JSONL root will block on this lock and emit monotonically-sequenced
cursors instead of corrupting the stream with duplicates. Readers take a
shared lock to avoid observing partially-written tail lines.

New deps on the event-store crate (postgres feature only):
tokio-postgres-rustls, rustls, rustls-native-certs, webpki-roots, url.
All four were already used elsewhere in the workspace; no new external
crates are introduced. File locking uses stdlib (Rust 1.89+ stable).

Regression tests added:
- jsonl_replay_advances_next_cursor_past_trailing_filtered_records
- jsonl_concurrent_appenders_emit_monotonic_cursors_through_file_lock
- jsonl_bounded_replay_does_not_parse_the_whole_file
- postgres_store::tests::{local_postgres_urls_are_recognised,
  remote_postgres_urls_require_tls,
  unparseable_postgres_url_with_scheme_falls_closed_to_remote}

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(reborn_event_store): close TLS bypass for libpq keyword strings and http:// libsql in production

Two High-severity findings from PR nearai#3171 review:

1. is_local_postgres_url() previously returned true for any string without
   "://", so libpq keyword form like "host=db.example.com user=..." took
   the NoTls branch and connected over plaintext. Walk the keyword list,
   look at the actual host, and only treat localhost / socket paths as
   local. Tests cover remote keyword strings, socket paths, and missing
   host=.

2. Production profile previously accepted http:// libSQL URLs and forwarded
   the auth token in cleartext. Reject http:// for RebornProfile::Production
   with a typed error before the build call. LocalDev / Test still accept
   http:// for running against local sqld instances. Tests cover both.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(reborn_event_store): use deadpool-postgres pool for transparent reconnection

Replaces the single Arc<Client> in PostgresStore with a deadpool_postgres::Pool.
The previous design built one tokio_postgres::Client at startup; if the
underlying connection task exited (idle timeout, server restart, failover,
transient network drop), all subsequent append/read operations would fail
forever because nothing rebuilt the connection.

deadpool's Manager replaces broken Clients on the next pool.get() call, so
each call site sees a live connection without per-site reconnect logic.
This mirrors v1's connection-management story (src/db/postgres.rs,
src/db/tls.rs both use deadpool-postgres) and avoids two divergent
reconnection paths in one binary.

Addresses serrrfirat's Medium-severity finding on PR nearai#3171.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* chore(tests): fix post-merge clippy warning

* fix(reborn_event_store): close TLS-mode + libpq-config + JSONL fsync gaps

Three follow-ups from PR nearai#3171 round-3 review:

1. CRITICAL — remote Postgres TLS not actually enforced. Passing a rustls
   connector is not enough on its own: tokio-postgres only consults the
   connector when Config::ssl_mode is Prefer or Require, and `sslmode=disable`
   in the connection string returns a plaintext stream before TLS is
   attempted. Add `enforce_remote_ssl_mode`: reject Disable for non-local
   configs, and force Prefer (the default) up to Require so the server
   cannot decline TLS without failing the connection.

2. HIGH — local/remote detector missed valid libpq forms. Re-parsing the
   raw connection string failed on `hostaddr=10.0.0.5` (numeric-IP keyword,
   no `host=` entry), `postgresql:///db?host=db.example.com` (URL with
   empty authority + host in query), and `host=/var/run/postgresql,
   db.example.com` (mixed socket/TCP list). Replace with
   `is_local_postgres_config` that walks the parsed `Config::get_hosts()`
   and `Config::get_hostaddrs()` so all libpq normalisations land in the
   same code path.

3. MEDIUM — JSONL append fsynced file contents but not the parent
   directory entry. On POSIX the new file's directory entry must also be
   fsynced for crash durability; without it the first append can vanish
   after a power loss even though `append()` returned success. Detect
   first-create via `path.exists()` pre-open and fsync the parent dir
   after `sync_data()`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(reborn_event_store): address PR nearai#3171 round-3 review findings

Closes the remaining libSQL/Postgres/JSONL gaps surfaced in the latest
serrrfirat review pass on PR nearai#3171.

libSQL production gate (#34, #36, #41):
- Reject `:memory:` for Production via the InMemory error variant.
- Case-insensitive scheme matching for `HTTP://` / `HTTPS://` /
  `LibSQL://`, so a mixed-case URL no longer falls through to
  Builder::new_local and silently creates a node-local SQLite path.
- Reject bare hostname-like values (e.g. `db.example.com`) for
  Production via a new ProductionLibsqlAmbiguousTarget error; LocalDev
  still accepts ergonomic forms like `events.db`.

libSQL backend hardening (#45, #49, #50):
- Skip `create_dir_all("")` for `events.db` in cwd.
- Enable `journal_mode=WAL` + `synchronous=NORMAL` once at build time
  for file-backed local stores so a long replay reader can no longer
  block writer commits past `busy_timeout`.
- Mirror the v1 retry pattern (`src/db/libsql/mod.rs::connect`): three
  attempts with exponential backoff so concurrent transient
  "unable to open database file" errors do not surface as durable-log
  failures.

Postgres backend hardening (#46):
- Honor `sslmode=require` for loopback configs so a TLS-only local
  Postgres / loopback proxy is accepted instead of forced to NoTls.

JSONL hardening (#38, #40, #42, #43, #44):
- Make JSONL constructors crate-private so production composition
  cannot bypass the single-node-durable acceptance gate.
- Hash path components (SHA-256 / 16-hex-char prefix + 32-byte URL-
  encoded hint) so case-distinct IDs (`Alice` vs `alice`) cannot
  collide on case-insensitive filesystems and 256-byte scope IDs no
  longer overflow the 255-byte filename limit.
- Snapshot file length before write/flush/sync; truncate back on any
  error so a partial write never leaves a torn JSON tail that wedges
  every subsequent append.
- Create directories with `0o700` and stream files with `0o600` on
  Unix so durable history is not world-readable under the typical
  `umask 022`.

SQL replay correctness (#37):
- After fetching filtered rows, run a small unfiltered COUNT over the
  scanned cursor window in both libSQL and Postgres backends; mismatch
  surfaces a missing entry row as `EventError::ReplayGap`. JSONL
  already detects this via line-by-line cursor sequencing.

Regression tests:
- libSQL/Postgres production gate (case-insensitive scheme, in-memory,
  bare hostname).
- Hashed-path-component case distinctness and length boundedness.
- Atomic JSONL append: failed serialise leaves file at pre-append
  length and the next append still advances cursor cleanly.
- Unix `0o700` JSONL root permissions.
- libpq quoted single-quote socket path is local; whitespace-around-
  `=` keyword strings classify remote correctly.
- libSQL replay surfaces a deleted entry row as `ReplayGap`.

Deferred with explicit acknowledgement:
- #39 (`ReadScope.invocation_id`) — module-doc note; needs a
  cross-crate change to `ironclaw_events` plus every replay caller.
- #48 (replay holds writer-blocking locks) — inline note at the JSONL
  read path; needs a stream-bytes-snapshot redesign coordinated with
  the durable-log contract.

Test plan
- cargo test -p ironclaw_reborn_event_store
- cargo test -p ironclaw_reborn_event_store --features "libsql postgres"
- cargo test -p ironclaw_host_runtime
- cargo test -p ironclaw_events
- cargo test -p ironclaw_architecture reborn_crate_dependency_boundaries_hold
- cargo clippy -p ironclaw_reborn_event_store --all-targets -- -D warnings
- cargo clippy -p ironclaw_reborn_event_store --features "libsql postgres" --all-targets -- -D warnings
- cargo clippy -p ironclaw_reborn_event_store --no-default-features --all-targets -- -D warnings
- cargo clippy -p ironclaw_host_runtime --all-targets -- -D warnings
- cargo fmt --all -- --check

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: serrrfirat <f@nuff.tech>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant