CHANGELOG.md (26557B)
1 # Changelog 2 3 All notable changes to the Radroots core libraries are documented in this file. 4 5 ## [1.0.0-alpha.1] 6 7 This alpha is not published until the repository release preflight and external 8 publish policy both pass for the same source revision. 9 10 ### Changed 11 12 - Event-store schema initialization now uses a transactional, checksummed 13 migration authority with exact legacy-baseline adoption, shared-database 14 catalog scoping, tamper-evident fail-closed managed history, exact catalog 15 deltas, SQLite and FTS5 integrity validation, a no-write current-schema fast 16 path, and read-only schema status inspection. Rollback is a terminal, 17 pool-closing maintenance operation with a public version floor. Raw migration 18 SQL and unrestricted destructive rollback are no longer public APIs. 19 - Event-store schema version `2` now installs a byte-pinned NIP-09 20 reconciliation hook over immutable NIP-01, addressable-feed-v1, and 21 registry-v7 semantics. The hook re-verifies durable raw authority and 22 persists generation-partitioned event coordinates, deletion requests, 23 normalized targets, canonical addressable state, and append-only 24 transitions without rewriting or deleting raw events. Projection cursors 25 now bind to the active source generation; version or generation changes use 26 a typed, revision-bound rebuild ticket that rejects stale, replayed, raced, 27 and ABA-replaced resets. Initial reconciliation and repeated rebuilds use a 28 marker-first transaction whose deferred commit barrier rejects partial 29 authority; every successful rebuild appends a fresh generation while 30 preserving immutable generation and transition history. Critical owned, 31 borrowed-savepoint, and extension wrapper bodies now bind production AST 32 identity, as do the isolated reconciliation-core and raw-head storage 33 modules. Post-core orchestration receives a private transaction capability 34 instead of SQLx authority; its fixed literal-SQL methods confine 35 trade/observation writes to declared operation/table pairs. A lightweight 36 source/transition/schema seal runs after that capability is dropped and 37 rejects protocol-authority drift without introducing per-ingest full-table 38 scans. Schema, pool, status, and ingest boundaries bind governed access to 39 SQLite `main`, reject ASCII-case-insensitive temporary-schema collisions, 40 preserve unrelated shared-schema foreign-key evidence, and retain both the 41 primary ingest and rollback errors when rollback also fails. 42 - Transport targets and Reticulum destinations now keep identity-bearing 43 fields private, expose read-only accessors, and revalidate canonical URI, 44 scope, label, routing, and fingerprint invariants during deserialization. 45 Nostr relay targets now use strict ASCII host, port, path, default-port, and 46 IPv6 canonicalization shared with the relay adapter. Policy-free relay URL 47 and target-set serialization has been removed; duplicate target sets fail, 48 and fetch requests require a typed nonempty target set. Adapter fetch items 49 are canonicalized and must belong to that request before any store mutation, 50 while the request timestamp is the sole observation-time authority. Public 51 relay policy is explicitly for trusted configured `wss` hostnames and 52 rejects local, special-use, single-label, and forbidden literal 53 destinations; localhost policy accepts exact loopback hosts only. The 54 default SDK connector does not make attacker-controlled DNS names an SSRF 55 boundary. Event-store diagnostic messages must be caller-redacted, 56 canonical, control-free, nonempty, and no larger than 4 KiB; automatic relay 57 publish observations no longer persist remote outcome text. Observation v1 58 remains endpoint-level and intentionally does not represent scoped 59 Reticulum or local-target identity; scoped evidence stays in transport 60 delivery receipts. This 61 intentionally changes fingerprints and public APIs for spellings and 62 configurations previously accepted. Because this is an unreleased alpha 63 contract, databases and serialized configuration containing those legacy 64 identities are not migrated: development instances must be reset and 65 canonically reseeded rather than silently reinterpreted. 66 - Geocoder locality and reverse results now expose administrative subdivision 67 identifiers as opaque strings. SQLite integer and text values normalize to 68 the same lossless public representation, and mixed-storage candidate order 69 remains deterministic. 70 - Default GeoNames asset installation now uses cancellable asynchronous DNS, 71 explicit connect, response, read, and total deadlines, denied redirects, and 72 bounded runtime shutdown. HTTP bodies stream through incremental length and 73 SHA-256 verification into a same-directory tempfile that is synced, checked 74 for SQLite integrity and schema, and atomically persisted. Public download 75 errors expose stable typed phases and detail fields instead of 76 `reqwest::Error`; trusted injected fetchers retain a bounded byte adapter. 77 - Trusted event-contract admission now has one signature-verified entry point. 78 Profile, root Post, Reply, Comment, DeletionRequest, and FoodAvailability 79 retain typed admitted values; other registered events require full contract 80 shape validation, while unsupported matching and invalid shapes remain 81 distinct failures. 82 - Event-store ingest now verifies before durable admission, retains every 83 verified durable candidate for registry-independent raw-head reduction, and 84 separates immutable valid-stream replay from current visibility. Explicit 85 raw, valid, raw-head, visibility, and visible-head APIs replace ambiguous 86 projection/head reads; projection cursors require an expected version and a 87 monotonic prior-sequence compare-and-swap. Verified ephemeral events receive 88 an explicit not-persisted outcome and allocate no raw sequence, tags, 89 observations, or heads. Read-only consumers can inspect the same fail-closed 90 status summary from an initialized pool without duplicating schema-sensitive 91 SQL or running migrations. 92 - Nostr fetch-ingest receipts now report exhaustive verification, contract 93 admission, valid-stream, and current-visibility outcomes independently. 94 Verification failures no longer share an `invalid` bucket with contract 95 failures, unsupported admissions retain their stable code without masking 96 visibility, and persisted events obtain visibility from the event store's 97 central authority. Fetches enforce a 64,000 raw-event ceiling, a 64 MiB 98 aggregate raw-JSON prefix budget, and the 256 KiB per-event wire limit before 99 Radroots parses adapter raw JSON; after upstream SDK frame decoding, the 100 official SDK stream applies the same retained-prefix bounds before retaining 101 serialized adapter output. Local event-store failures abort fetch ingest as 102 operational errors instead of being reported as malformed relay input. 103 - Generic outbox APIs now reject every NIP-16 ephemeral event before durable 104 queue persistence. Live-only events, including NIP-42 relay-auth and NIP-98 105 HTTP-auth signatures, remain owned by their transport exchanges. Externally 106 supplied SQLite pools now validate their backing mode and configure every 107 connection before migration or writes. 108 - Retired trade order-workflow and product-projection source files that were no 109 longer compiled or exported have been removed. Current FoodAvailability 110 projection ownership remains with the event store. 111 - Event-store schema v3 adds one central current-visibility authority, a 112 generation-bound addressable transition feed, and an atomic focused 113 FoodAvailability projection with bounded FTS search. The successor contract 114 authenticates schema `0003`, registry-v7 admission, exact kind scope `30402`, 115 executable transition/projection vectors, and the frozen NIP-09 predecessor. 116 Stored Blossom image digests use the public typed SHA-256 value. 117 - Event-store schema v4 adds a persisted raw-source capacity seal for event 118 rows, tag rows, and their governed UTF-8 text bytes. Unique durable ingest 119 now refuses prospective capacity excess before mutation, database reopen 120 performs a bounded full recount, and independent file pools serialize an 121 exact final capacity slot. Every supplied main database must report UTF-8 122 before schema or journal mutation. Retained source history stops at eight 123 generations before requesting fresh-store replacement and resync, and 124 production rollback cannot cross the migration that introduced that 125 append-only history. The 126 authenticated SourceMaintenance successor binds the immutable schema-v3 127 predecessor, migration and runtime sources, breaking capacity-error API 128 replacements, and an executable result vector. Schema v4 is intentionally 129 non-additive: it replaces exactly the Food projection delete guard, Food image 130 delete guard, and source rebuild-marker insert guard, requires that exact 131 symmetric catalog delta, and restores the exact v3 trigger SQL on rollback. 132 A drifted v3 predecessor is rejected atomically rather than repaired during 133 upgrade; repair authorization is reserved for a future rebuild after exact 134 managed-v4 catalog, ledger, and migration history plus immutable raw/source 135 lineage and capacity validation. Derived hook state is the repair target, 136 not a repair precondition. The former 137 `RadrootsEventStoreReconciliationResource` type and 138 `ReconciliationCapacityExceeded` error variant are replaced by the 139 versioned source-capacity resource and typed capacity/history errors. 140 - Bare-envelope replica ingestion is quarantined behind the explicit, 141 non-default `legacy-ingest` feature. Default replica APIs expose emit and sync 142 surfaces only; a future product ingest boundary must consume a store-produced 143 verified, valid-stream-eligible, currently visible admission. 144 - Blossom blob URLs now validate complete raw Unicode text before URL parsing 145 and exact raw ASCII DNS label grammar before returning a typed value. Unicode 146 control/format text, implicit IDNA conversion, empty labels, underscores, 147 edge hyphens, and oversized DNS names can no longer enter approved or 148 byte-verified media typestates; URL-parser-valid explicit ASCII punycode and 149 canonical IP authorities remain supported. 150 - NIP-99 kind `30402` now has an explicit two-level taxonomy: the standard 151 protocol kind and coordinate are **Classified Listing**, while the richer 152 Radroots farm, bin, inventory, and price profile is **Operational Listing**. 153 Public constants, types, functions, modules, operation IDs, and generated DTO 154 roots use those unambiguous names with no legacy `listing` aliases or modules. 155 - Operational listing decoding now has one tag-authoritative implementation in 156 `radroots_event_codec`. The typed parts decoder reports the established 157 listing error taxonomy, and JSON content can no longer override canonical 158 product, inventory, or bin tags in trade and replica consumers. 159 - Operational listing authoring now emits canonical Markdown content from the 160 tag-authoritative model. Tolerant inbound JSON inspection remains a decode 161 compatibility boundary only and is not an authoring format. 162 - Operational listing trade validation exposes one shared unsigned-model 163 semantic reducer and a signature-verified event boundary that delegates to 164 it after kind, marker-partition, and decoding checks. Event-store projection 165 reconstructs and verifies the event typestate instead of trusting a plain 166 stored envelope. Canonical authoring now invokes that reducer before draft 167 construction and preserves its typed failure cause; the reducer rejects 168 duplicate bin IDs and invalid quantity or price semantics in every bin. 169 - Generic NIP-01 identifier and signature verification is now independent of 170 knowledge decoding, and every dynamic Nostr kind conversion rejects values 171 above `65535` instead of truncating them. Canonical-length author keys that 172 are not valid secp256k1 curve points now return `malformed_envelope` instead 173 of `signature_invalid`. 174 - Calendar authoring and admission now use explicit NIP-52 authored, parsed, and 175 admitted states for date events, time events, calendars, and RSVPs. Kind 176 `31922` no longer emits uppercase `D`; kind `31923` derives integer UTC-day 177 `D` values from its validated time range. 178 - Authored profile and calendar media now share the byte-verified Blossom image 179 proof type; unverified URLs remain inbound data and cannot enter authoring APIs. 180 - Root kind-`1` product admission now verifies NIP-01 identity and signatures, 181 separates every `e`-tagged event as a thread-excluded candidate without a 182 Reply claim, and deterministically admits Ask, PhotoUpdate, or Update roots 183 while preserving malformed media diagnostics. 184 - NIP-10 Reply authoring now requires an opaque direct or nested type and emits 185 exact marked `root`/`reply` event references plus required participant 186 references. Verified inbound projection accepts preferred marked and 187 deprecated positional threading, preserves valid supplemental references as 188 citations, and retains malformed advisory metadata as ordered diagnostics 189 while keeping every admitted Reply out of root-card classification. 190 - Authored and projected NIP-10 Reply and NIP-22 Comment relay hints now share 191 `RadrootsNostrRelayHint`, one portable, canonical visible-ASCII WebSocket URL 192 profile instead of generic URL normalization. Strict authoring rejects 193 noncanonical hints; tolerant verified projection preserves rejected hints 194 verbatim in ordered raw-tag diagnostics. Relay syntax remains separate from 195 each event profile's wire-size budgets. 196 - Kind `1111` Comment handling now uses distinct opaque authored, verified 197 projection, admitted-event, and sealed Nostr publication states. The strict 198 NIP-22 profile supports only event or address roots for classified-listing 199 kind `30402` and calendar kinds `31922` and `31923`; ordinary kind `1` and 200 external `I`/`i` references are rejected. Legacy pseudo-thread tags carry no 201 Comment authority, are never authored, and remain raw supplemental input. 202 - Typed root posts now enter signing and client publication through an opaque 203 builder with no raw tag/content mutation. Generic builder direct signing and 204 client publication reject kind `0` plus every kind `1` before signer access; 205 externally supplied unsigned events, NIP-46 signing, and signed-event relay 206 remain explicit low-level Nostr interoperability with no typed product 207 authoring claim. 208 - Frozen drafts now carry event-contract registry version `7`, revalidate all 209 persisted fields and the recomputed event id during deserialization and 210 signing, and enforce explicit `GenericDraft`, `TypedOnly`, and `ReadOnly` 211 authoring policies. 212 - Event wire and envelope admission now reject more than 4,096 aggregate tag 213 elements, and SDK-event conversion returns the typed envelope error instead 214 of panicking on relay-controlled oversized input. 215 - Strict authored posts enforce the 256 KiB compact signed-event ceiling after 216 exact JSON escaping, in addition to per-element and aggregate decoded tag 217 limits. 218 - Workspace packages declare one governed version explicitly so mounted path 219 consumers preserve it, and every internal root dependency requires that exact 220 pre-release version. 221 - Conformance suites now identify the `1.0.0` event-contract generation. 222 - Release metadata records exact governed impacts for removed public types, 223 fields, functions, modules, constants, Cargo features, and trait 224 implementations, plus changed field types, constant values, and algorithms. 225 226 ### Added 227 228 - A fixed signed central-admission corpus executes every admitted variant, 229 Update/PhotoUpdate/Ask root classification, Post-to-Reply promotion, 230 Operational Listing fallback from Food exclusion, generic NIP-99 exclusion, 231 unsupported kinds, malformed registered shapes, and ambiguous Food markers. 232 - Generic protocol builders can now finalize into an opaque checked external 233 signing request. The request preserves the standard unsigned-event JSON wire 234 shape while preventing raw mutation or unchecked reconstruction, and it 235 accepts only an exact author/id match with a valid NIP-01 signature. 236 - Kind `30402` now has one allocation-free raw marker-name partition that 237 distinguishes focused FoodAvailability, richer Operational Listing, 238 marker-free generic NIP-99, and mixed ambiguous inputs before profile 239 tag-shape validation. 240 - FoodAvailability now has strict domain and authored-media input primitives 241 for bounded identifiers and text, canonical decimal price and quantity, 242 uppercase currency, the closed ten-unit food vocabulary, active or sold 243 status, timestamps, dimensions, and at most 64 unique byte-verified Blossom 244 images. Its typed codec emits exact kind-`30402` wire parts, while verified 245 tolerant admission normalizes compatible inbound values, preserves bounded 246 ordered image diagnostics, and excludes generic or operational listings. 247 Strict revision comparison revalidates both signed events against authored 248 wire semantics before enforcing a stable coordinate and `published_at` plus 249 NIP-01 replacement ordering. 250 - Focused FoodAvailability signing and client publication now use a sealed 251 Nostr builder with a construction-time timestamp and no raw mutation escape. 252 Generic signing and client publication reject focused or mixed kind-`30402` 253 profiles before signer access; signed-event relay remains transport-only. 254 Typed signing and publication do not attest BUD-02 upload completion. 255 - Behind the explicit non-default `legacy-ingest` feature, legacy replica 256 ingestion verifies kind-`30402` signatures, selects the raw addressable head 257 before profile decoding, and sends only the Operational Listing partition to 258 its trade-product projection. Selected focused/generic exclusions and 259 invalid/ambiguous rejections remove an older projection while advancing the 260 head, preventing stale projection fallback. The feature-gated public 261 head-only helper rejects kind `30402`; callers must use profile-aware legacy 262 ingestion so the head and projection remain atomic. These helpers are not a 263 Phase 1 product ingest boundary. 264 - Event-contract identification now selects Operational Listing only for its 265 raw marker partition. Focused FoodAvailability is admission-only, while 266 marker-free generic and mixed-marker NIP-99 events cannot be mislabeled as 267 operational contracts. 268 - Verified Profile admission binds a signed exact kind-`0` envelope to the 269 tolerant metadata projection, accepts standard tagless events, and exposes 270 deterministic equal-time lowest-id replacement vectors. 271 - Strict authored Update, PhotoUpdate, and Ask types emit deterministic kind-`1` 272 wire parts. Photo and Ask media require byte-verified Blossom image 273 descriptors, exact ordered NIP-92 metadata, bounded nonzero fields, and 274 same-digest approved fallback URLs. 275 - Raw signed kind-`1` conformance vectors prove signature-gated profile 276 admission, thread-candidate exclusion, classifier precedence, tolerant 277 metadata retention, and stable rejection codes. Operation-owned vectors also 278 execute every typed post authoring, projection, and admission function and 279 compare complete deterministic outputs. 280 - Raw signed NIP-10 vectors execute marked direct and nested Replies, marked 281 supplemental citations, deprecated positional empty-marker author hints, 282 malformed middle-citation tolerance, participant and relay validation, 283 classifier precedence, signature-gated admission, and stable invalid-case 284 codes through the public owning APIs. 285 - The fixed 114-case NIP-22 corpus executes all three governed Comment 286 operations with complete authored wire, verified projection, diagnostic, 287 admission, Unicode, precedence, and exact resource-limit expectations. 288 Projection and admission inputs are self-contained signed event JSON, and 289 the packaged fixture is byte-identical to the canonical contract vector. 290 - Generic NIP-01 coordinates now validate canonical 291 `kind:pubkey:identifier` values with the correct replaceable and addressable 292 kind rules. Strict NIP-09 authoring emits deterministic kind-`5` `e`, `a`, 293 and derived `k` tags, while tolerant verified projection preserves advisory 294 diagnostics and admission keeps the projection bound to its 295 signature-verified envelope. Fixed contract-owned conformance vectors cover the 296 authored, projection, admission, and resource-boundary operations. 297 - NIP-09 deletion requests now enter signing and client publication through a 298 sealed typed builder with no raw kind, content, or tag mutation. This surface 299 creates and transports a request only; it provides no target lookup, 300 authorship decision, deletion authorization, store mutation, relay-effect, 301 or deletion-effect semantics. 302 - NIP-09 suppression evaluation is now a separate pure operation over one 303 signature-verified candidate and admitted requests. It enforces same-author 304 direct-event and inclusive address-cutoff rules, keeps kind `5` immune, 305 ignores advisory kinds, returns canonical evidence independent of input 306 order, and never mutates raw events or storage. 307 - The NIP-09 reconciliation-v1 manifest and executable event-store result 308 vector pin migration `0002`, registry-v7 inventory, semantic vector inputs, 309 and the frozen verification, admission, head-selection, and suppression 310 source graph. 311 - Event-store NIP-09 migration now bounds every retained raw-source text field 312 and row count through matching preflight, in-lock, and paged-loader checks. 313 Capacity failure is typed and atomic, and exact-target indices avoid cloning 314 request payloads or scanning unrelated requests per candidate head. Rebuild 315 tickets can commit at their captured raw high-water while later events remain 316 available for ordinary catch-up. Schema opens validate every applied hook, 317 and composed callers can reserve the SQLite writer with 318 `begin_write_transaction` before reading and ingesting in one transaction. 319 Borrowed-transaction ingests use nested savepoints so a failed call cannot 320 leave partial event-store writes available for the caller to commit. 321 322 ### Removed 323 324 - The duplicate private operational listing parser in `radroots_trade` was 325 removed; trade validation now consumes the canonical event codec. 326 - The ambiguous `listing` source modules and public compatibility aliases were 327 removed. Consumers must migrate to the Classified Listing protocol names or 328 Operational Listing product names according to the API's responsibility. 329 - The public operational-listing JSON wire-parts authoring helper was removed; 330 product authoring uses the canonical Markdown wire-parts path. 331 - Legacy calendar event models and permissive calendar tag-builder authoring 332 paths were removed. 333 - Direct `RadrootsProfile` draft encoding and the identity profile publisher were 334 removed in favor of the validated authored-profile boundary. 335 - The `radroots_identity/profile` Cargo feature was removed with its embedded 336 legacy Profile projection. 337 - Replica sync no longer synthesizes Profile events from lossy stored 338 projections. Its transfer protocol is now version `2`, and request JSON 339 containing the removed `include_profiles` option is rejected. 340 - `RadrootsNostrClient` no longer implicitly dereferences to the upstream SDK 341 client. Narrow client operations and the explicit ownership bridge remain. 342 - `RadrootsNostrSignerBackend` no longer accepts a raw `nostr::EventBuilder`; 343 callers that implement standard external signer protocols must supply the 344 protocol's unsigned event explicitly. 345 - Permissive `RadrootsPost` tag authoring, the free-form Nostr post builder, and 346 the generic net custom publisher were removed. Product-root publication now 347 requires one of the strict authored Update, PhotoUpdate, or Ask states. 348 Generic kind-1 authoring is no longer available through the generic protocol 349 builder; non-product interoperability remains available only for 350 non-reserved kinds. 351 - The permissive post-reply builder and raw-string net reply publisher were 352 removed. Reply signing and client publication now require the typed NIP-10 353 Reply boundary. 354 - The legacy `RadrootsComment` DTO/Serde model, permissive Comment 355 encode/decode modules, and their public functions were removed. Comment 356 authoring now requires `RadrootsAuthoredNip22Comment`, while inbound use must 357 pass through verified projection or admission. 358 - The Reply-owned `RadrootsNip10RelayHint` name was removed. Callers must use 359 the shared `RadrootsNostrRelayHint` type for canonical Nostr tag relay hints. 360 361 ### Compatibility 362 363 - Callers that previously passed out-of-range `u32` kinds to Nostr builders or 364 job decoders now receive typed range errors instead of truncated kinds. 365 - Identity JSON containing the removed embedded `profile` projection is now 366 rejected, including the nested public-profile form, instead of being loaded 367 and later rewritten without that field. 368 Migrate the value to a signed kind-`0` metadata event before rewriting the 369 identity file. 370 - Replica-store backups and export manifests now record a stable schema 371 compatibility version instead of package SemVer. Existing backups stamped by 372 `0.1.0-alpha.2` remain restorable because this release does not change their 373 stored schema. 374 - Persisted frozen drafts with event-contract registry versions `1` through 375 `6` are rejected and must be reconstructed against registry version `7`; 376 strict Profile plus typed and read-only post contracts can no longer be 377 reconstructed as generic drafts. 378 - Generic builder signing and client publication reject kind `1111` before 379 signer access. Product Comment publication must use the sealed NIP-22 380 builder; relaying an already signed event remains a generic transport 381 operation without a typed authoring claim. 382 - Generic builder signing and client publication reject every kind `5` before 383 signer access. Product deletion-request publication must use the sealed 384 NIP-09 builder; externally supplied unsigned events, NIP-46 signing, and 385 relaying an already signed event remain low-level interoperability operations 386 without a typed authoring or deletion-effect claim. 387 - This breaking capsule revision must not be pinned or published through 388 downstream product clients until `radroots_app_rt` is migrated, generated FFI 389 bindings are rebuilt, and downstream compile and contract qualification pass. 390 - `radroots_event_from_nostr` now returns `Result<RadrootsEventEnvelope, 391 RadrootsEventEnvelopeError>`; callers must handle hostile or oversized SDK 392 events explicitly.