Changelog
All notable changes to XTR-on-Rust will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
Unreleased
0.3.0-rc - 2026-09-10
Third minor release. Ships the X-Road REST passthrough lane (issue #5) — XTR now fronts both X-Road SOAP and X-Road REST services from the same DSL directory, over the same mTLS identity. Spec-compliant per X-Road Message Protocol for REST v1.0.4.
Fully backwards-compatible with existing 0.2.x SOAP DSLs. See
the migration reference in this book for the (small) surface of
externally-visible changes and their recovery flags.
Added
- REST passthrough lane (issue
#5). Spec-compliant
implementation of the
X-Road Message Protocol for REST v1.0.4.
DSL files declare
kind: restwith atarget:block; XTR constructs the/r1/{instance}/{class}/{code}/{subsystem}/{service_code}[{path}]URL (§4.1) with percent-encoded identifier segments (§4.2), setsX-Road-Client(§4.3) andX-Road-Id, and forwards all inbound headers (Accept, Content-Type, Cache-Control, X-Road-UserId, user-defined) unmodified (§4.3). Query params pass unmodified by default (§4.5), with an optional DSL-level allow-list. Response returns as-is with all upstream X-Road response headers (X-Road-Service,X-Road-Request-Hash,X-Road-Error, etc.) passed to the caller. Redirects pinned toPolicy::none()per §4.4. See the "REST passthrough" chapter of the mdBook for the operator-facing setup guide. security_server.trust_ca_pathconfig field. Real X-Road Security Server TLS certs are typically issued by an operator-managed private CA that isn't in the system trust store; this field points at a PEM bundle so the mTLS handshake can verify the server cert. Applies to both SOAP and REST lanes.XtrError::MethodNotAllowed(405) — emitted when the DSL-declared method doesn't match the inbound HTTP method. Applies to both SOAP (POST-only) and REST DSLs. Previously axum routed non-POST to a bare 405; the newanyroute needs an explicit variant so the wire shape ({error, message}) stays consistent.- Doctor rules for the REST lane:
fatal-rest-no-security-server,fatal-rest-ss-not-https,fatal-rest-target-fields-missing,weak-rest-identifier-charset(per spec §4.8),info-rest-lane-ready,info-rest-trust-ca-system. - Full-mTLS integration test (
tests/it_rest_mtls.rs) —rcgen+openssl(PKCS12) +tokio-rustls(client-cert verifying server) drive the productionRestLaneExecutor::newcode path end-to-end with a real handshake. Complements the plain-HTTP router tests intests/it_rest_passthrough.rs.
Changed
XRoadTemplateis now{ method, kind: TemplateKind }whereTemplateKindisSoaporRest. Existing DSL files without akind:field deserialise asSoap— full backward compatibility.POST /:group/:serviceroute widened toany /:group/:service. DSLmethod:is now enforced at the handler; mismatches return405 method_not_allowedfor both SOAP and REST kinds.Executorgaineddispatch_rest(); existingdispatch()renamed todispatch_soap(). External callers were only via the router.- The mTLS client builder is now a shared
build_mtls_client()helper used by bothSecurityServerExecutor(SOAP) andRestLaneExecutor(REST). Applies redirect policy, TLS floor, decompression posture, and CA bundle from one place.
0.2.0-rc.1 - 2026-09-06
Hotfix — Dockerfile ENTRYPOINT / CMD interaction broke the
subcommand shape documented in MIGRATION.md and
book/src/doctor.md. The recipe
docker run --rm turnerrainer/xtr:0.2.0-rc doctor was
supposed to invoke the doctor subcommand but instead tini
tried to exec a non-existent doctor binary — the tini
error surfaced was
FATAL tini (7) exec doctor failed: No such file or directory.
Discovered when local-testing the just-published
0.2.0-rc image against the operator flow.
Fixed
Dockerfile:ENTRYPOINT ["/usr/bin/tini", "--", "/app/xtr-on-rust"]CMD []. Extra args todocker runnow APPEND as argv to the binary instead of REPLACING CMD. Baredocker run <image>still boots the server (no argv → server path in main.rs).
tests/dockerfile_entrypoint.rs(new): contract test parses the Dockerfile and refuses any shape where ENTRYPOINT doesn't pin the binary. Guards against this class of regression before publish.
Everything else about 0.2.0-rc still applies — see below.
0.2.0-rc - 2026-09-06
Third release candidate. Closes the h2ck.me pre-publication audit (v1) — two Critical, four High, five Medium findings on the WSDL trust boundary. Because several fixes changed externally-visible behaviour (SOAP fault response shape most notably), this ships as a minor bump, not a patch.
Snyk-driven base-image bump debian:bookworm-slim →
debian:13.6-slim (PR #1) is included; runtime-verified with
end-to-end TLS calls to real Ariregister.
Migration from 0.1.0-rc.2
Read MIGRATION.md and run
xtr-on-rust doctor (new subcommand — see below) against
your xtr.yaml. The doctor prints a per-finding table
(FATAL / BREAK / WEAK / INFO) with exact recovery flags.
Breaking changes vs 0.1.0-rc.2
Four externally-visible changes. All have recovery flags for strict behaviour equivalence; the defaults were changed because the safer posture is a better fit for a public release.
-
SOAP fault response shape (H3). The JSON body for a
502 upstream_soap_faultno longer includes thedetailfield;string(faultstring) is capped at 200 characters;messageis shortened from"upstream returned SOAP Fault (X): Y"to"upstream returned SOAP Fault (X)".- Server logs still carry the full fault detail at
warn!level via structuredtracingfields. - Recover exact-equivalence by setting
expose_soap_fault_detail: trueinxtr.yaml.
- Server logs still carry the full fault detail at
-
xroad_protocol_versionenum validation at boot (M1). Values other than"4.0"or"4.1"now hard-fail startup with an error naming the bad value and the accepted set.- Default remains
"4.0"; vast majority of configs unaffected. Empty string, typos, or a value set by an operator experimenting with a newer protocol will now refuse to boot. - Recover by setting
xroad_protocol_versionto one of the accepted values.
- Default remains
-
X-Road sidecar identity validation (H2). If a
<wsdl>.meta.yamlsidecar declaresmember_class,member_code, orsubsystem_code, they must equal the correspondingclient_datafield inxtr.yaml. Mismatch → whole WSDL is skipped with a WARN. Emptyclient_datafields (default state) skip the check per-field so pre-onboarding operators still get all endpoints.- The shipped
xtr.yamluses placeholdermember_code: "<your-registry-code>". Any real sidecar with a real member code will now be rejected against the placeholder — setclient_databefore deploying with sidecars. - Recover by either aligning sidecar values with
config OR removing the identity fields from the sidecar
(sidecar can still override
service_code/service_url).
- The shipped
-
URL guard on WSDL upstreams (C1). Every URL discovered in a WSDL
<soap:address location=…>or metadata sidecarservice_url:override is validated at ingest. Private, loopback, link-local, CGNAT, ULA, IPv4-mapped-IPv6, and non-http(s) schemes are rejected; the offending URL is dropped from the DSL (WSDL operations then fall back to Security Server routing). Also,http://upstreams are rejected by default.- Recover by setting
wsdl.allow_http_upstream: truefor plaintext HTTP upstreams, or by adding legitimate internal hostnames towsdl.upstream_host_allowlist. Private-IP upstreams cannot be recovered — that is by design.
- Recover by setting
Behaviour changes worth calling out
Not breaking in the SemVer sense (unlikely to affect real deployments) but observable if you're at an edge:
- HTTP client no longer decompresses response bodies
(M2). Both executors now build the reqwest client with
.no_gzip(),.no_brotli(),.no_deflate(). reqwest's default WAS to decompress transparently, which would let a 16 MiB wire-body cap silently protect a much larger in-memory payload. If any upstream sendsContent-Encoding: gzipunconditionally, XTR now surfaces the compressed bytes to the XML parser and it will error. No recovery flag; if you hit this, open an issue and we'll add one. - XML depth cap 512 → 128. Real X-Road envelopes are single-digit-deep; 128 leaves ~10x headroom while keeping the debug-build test-thread stack safe.
- Schema-include filenames restricted to
[A-Za-z0-9._-]+. Symlinks under the WSDL dir are also rejected. Any WSDL corpus that ships XSDs with non-ASCII filenames or symlink-organized includes now silently drops those includes.
Added
xtr-on-rust doctorsubcommand — new. Validates the operator'sxtr.yamlagainst the audit-v1 ruleset and the known breaking-change recovery matrix. Emits FATAL / BREAK / WEAK / INFO findings; exit code 1 on any FATAL (or on WEAK under--strict). SeeMIGRATION.mdfor the recipe.MIGRATION.md— machine-readable + human-readable guide for both operators and LLMs walking through the 0.1 → 0.2 upgrade.
Security — h2ck.me audit-v1 fixes (2026-09-05)
Pre-publication audit findings from h2ck.me/projects/XTR/v1.
Two Critical, four High, five Medium — all closed on this branch.
- C1 (SSRF via WSDL upstream URL) — new
src/wsdl/url_guard.rsvalidates every URL discovered in a<soap:address location=…/>or metadata-sidecarservice_url:override. Rejectshttpby default, private / loopback / link-local / CGNAT / ULA / v4-mapped-v6 ranges, and non-http(s) schemes. New config:wsdl.allow_http_upstream,wsdl.upstream_host_allowlist. - C2 (XML bomb safety net) — added
MAX_XML_EVENTS = 100_000per-document event budget insrc/translate/xml_to_json.rs. Complements the existing depth cap (lowered from 512 → 128 for debug-stack safety) and the pre-existing custom-entity rejection. Regression test bombs → error, no expansion. - H1 (path traversal in WSDL schema-include loader) —
resolve_local_schemanow applies filename charset restriction, symlink rejection viasymlink_metadata, and canonicalisation withstarts_with(wsdl_dir)containment check. - H2 (X-Road client impersonation via sidecar) — sidecar
member_class/member_code/subsystem_codeare now validated againstclient_dataat load time. Mismatch → refuse to load the sidecar (WSDL is skipped with a WARN). - H3 (SOAP fault detail leak) —
IntoResponseforUpstreamSoapFaultnow stripsdetailand capsfaultstringat 200 chars by default. Full detail always logged atwarn!level for operators. Configexpose_soap_fault_detail: boolre-enables the raw response for internal debugging. - H4 (TLS defaults not tested) — both executors now pin
min_tls_version(TLS_1_2)explicitly on the reqwest builder. Addedtests/tls_defaults_enforced.rsintegration test (post-h2ck.me-v1 nit): spins a self-signed TLS server viarcgen+tokio-native-tlson a random port, asserts a default-trust-store reqwest client refuses the handshake AND a bypass-flagged client succeeds against the same server (counter-test guards against false-positive greens from a broken server setup). - M1 (xroad_protocol_version typos) —
AppConfig::validate()called at boot; rejects any value not in{"4.0", "4.1"}with an error naming both the bad value and the accepted set. - M2 (gzip invariant) — both executors set
.no_gzip(),.no_brotli(),.no_deflate()so the 16 MiB wire-body cap inread_boundedstays meaningful regardless of upstream content-encoding. - M3 (attribute-safe Handlebars helper) — registered
{{xml_attr foo}}helper that emits" ' < > &for interpolation into XML attribute values. Default{{foo}}is still safe in element text.
New crate dependency: url = "2" (already a transitive of
reqwest — promoted to a direct dep for url_guard.rs).
0.1.0-rc.2 - 2026-07-29
Second release candidate. Adds runtime WSDL folder-drop
(task 013), ships real Ariregister WSDL + 34 companion XSDs
under wsdl/ar/ as the canonical source of truth (yields 33
auto-generated /ar/* endpoints on every boot), and renames
all "SS" abbreviations to "Security Server" throughout code,
config, DSLs, docs, and task files.
Added — Task 013 WSDL folder-drop
wsdl_watch_dirconfig field. At boot, XTR scans<dir>/<group>/*.wsdl, parses each, and generatesDSL/<group>/<operation>.ymlperwsdl:operation.- SOAP-1.1 document/literal parser in
src/wsdl/— supports inline anonymous complexTypes, named top-level complexTypes (with lazy resolution + cycle guard),xsd:includevia a local-filesystem loader (offline discipline preserved),xsd:importskipped as framework,xsd:annotationskipped as documentation. Bail-out-on-unsupported forxsd:choice, WSDL 2.0, RPC/encoded, MIME attachments. - Per-op lenient: unresolvable input elements log WARN and skip that operation; sibling operations still generate.
- Deterministic YAML output — same WSDL always produces byte-equal bytes.
- Generated DSLs carry a marker header. Hand-written DSLs (no marker) always win on collision with a WARN log.
- Optional
<wsdl>.meta.yamlsidecar opts into X-Road envelope wrapping (member_class/member_code/subsystem_code → auto-generated<xroad:*>header block). - Generator recognises the X-Road
TURVASERVERplaceholder in<soap:address location=…/>and omitsservice:so the executor routes viasecurity_server:instead.
Added — WSDL as source of truth
wsdl/ar/— real Ariregister WSDL + 34 companion XSDs (~180 KB) vendored into the repo.xtr.yaml(new) — default config that ships with the repo.docker compose up/cargo runnow boots with 33 Ariregister endpoints live via WSDL ingestion..gitignore—/DSL/ar/*.ymlignored (regenerated per boot from the WSDL). Hand-written DSLs (likeDSL/xroad/*) stay tracked.- Removed the 4 previously hand-written Ariregister sample
DSLs (
lihtandmed_v3,detailandmed_v2,ettevottegaSeotudIsikud_v1,tegelikudKasusaajad_v2) — now auto-generated with WSDL-native param names (Estonianariregistri_koodinstead of Englishreg_code).
Changed — SS → Security Server
Renamed every "SS" abbreviation to "Security Server" across
code, configs, DSLs, book chapters, task files, comments,
and CHANGELOG entries. Rationale: the "SS" abbreviation
carries a well-known historical reputation that reads
unprofessional in a European government-infrastructure
context. See feedback_never_abbreviate_security_server.md.
SsExecutor→SecurityServerExecutorsrc/executor/ss.rs→src/executor/security_server.rsExecutor.ssfield →Executor.security_server- All prose in
book/,docs/,tasks/, comments — same. - SOAP protocol literals unchanged (
SOAP-ENV:Server,env:Serveretc are external error codes and must stay as-is).
Docs
book/src/ops/wsdl-ingestion.md— folder layout, marker semantics, override rules, X-Road sidecar convention, "no admin HTTP endpoint" rationale.book/src/dsl/adding-a-service.md— reframed as override fallback path; WSDL-drop is primary now.
Verified: 82/0/0 tests, fmt clean, clippy -D warnings clean, mdbook + linkcheck build clean, live smoke boots with 35 endpoints (33 auto-generated Ariregister + 2 hand-written X-Road samples).
0.1.0-rc.1 - 2026-07-28
First publishable release candidate. Working REST → SOAP → X-Road proxy in Rust, live-verified against public Ariregister endpoints. Everything below in this section is what ships in this tag.
Added — Post-MVP hardening sweep (2026-07-28)
Landed tasks 003, 005, 010, 011, 012, and a follow-up security sweep in a single day. Test count 29 → 51 (0 fail, 0 ignored).
Task 010 — SOAP Fault detection. HTTP 200 + <soap:Fault>
now maps to a structured 502 upstream_soap_fault with
code / string / detail top-level fields, instead of silently
being translated as a successful response. Handles SOAP 1.1 and
1.2 including namespace-prefixed variants and xml:lang-tagged
Reason elements.
Task 011 — Request/response size caps + timeout config. New
limits: config section (max_request_bytes 1 MiB,
max_response_bytes 16 MiB, request_timeout_secs 30). Inbound
overflow → 413 request_too_large; upstream overflow → 502
upstream_body_too_large with the connection torn down
immediately. Outbound responses read chunk-by-chunk via a new
read_bounded helper — bounded memory per request.
Task 012 — JSON type coercion. Bare integer leaves become
Value::Number; true/false become Value::Bool. Deliberate
non-goals with enforcing tests: no float coercion (precision loss
on "3.10"), no leading-zero coercion ("007" stays string —
those are opaque IDs), no case-insensitive booleans, i64
overflow keeps raw string, attributed-leaf #text stays string.
Task 005 — Explicit X-Road protocol version in config. New
xroad_protocol_version: "4.0" config field exposed as
{{generate.protocol_version}} in the Handlebars auto-context.
The two shipped X-Road DSL samples (listMethods,
allowedMethods) migrated to the auto-context variable —
protocol-version changes now require a single config line update
instead of touching every DSL.
Task 003 — Content-Type + charset on outbound calls. Closed
as landed with task 002 Phase D — both executors already set
text/xml; charset=utf-8; existing integration test already
captured + asserted it. Marker added to done/.
Security sweep
quick-xml 0.36 → 0.41. cargo audit flagged two
high-severity DoS advisories (RUSTSEC-2026-0194 quadratic on
duplicate attribute names, RUSTSEC-2026-0195 unbounded
namespace-declaration allocation) — both fixed in 0.41. Both
directly relevant since XTR parses untrusted upstream XML on
every request; size caps alone don't help against the quadratic
runtime.
XXE guard. quick-xml 0.41 introduced Event::GeneralRef
for entity references outside the XML-predefined set. Character
references (&#nnn;, &#xhh;) resolve to Unicode codepoints
via a new decode_char_ref helper. Custom entities
( , ©) are rejected with an explicit
XmlParseError mentioning XXE risk — accepting them would
require a DOCTYPE, which is the XXE attack surface.
Nesting-depth cap (MAX_NESTING_DEPTH = 512) on
parse_children. Prior state: unbounded recursion — a document
with hundreds of thousands of <a><a><a>… levels blew the
stack. Real envelopes rarely exceed 10 levels; cap gives ~50x
headroom.
Regression coverage added: Handlebars single-pass re-render safety, malformed-body handling (7 shapes), percent-encoded-slash path traversal, XML nesting cap, hex character ref, custom entity XXE guard.
Final audit posture: cargo audit 0 advisories, cargo deny check green on advisories/bans/licenses/sources.
Added — Task 002 MVP (v0.1.0-rc.2 candidate)
Working REST → SOAP → X-Road proxy per DESIGN.md §8. Implements the module tree, HTTP surface, DSL loader, Handlebars expansion, executor (plain + mTLS), XML → JSON translation, auto-generated OpenAPI, and integration tests. 12 of the 17 JVM XTR bugs from DESIGN.md §7 fixed:
#1 subsystem_code (correctly spelled)
#2 no @Value on statics — instance-field config
#3 Handlebars: single-pass render with merged context
#5 xroad:client element built correctly (no literal %s)
#6 system trust store (no trust-all X509TrustManager)
#7 response exposes both {body, headers}
#8 route pattern is /:group/:service (not wildcard)
#9 structured error responses ({error, message} + proper status)
#10 /health endpoint
#13 port 8080 everywhere (no 9010/9020/8080 confusion)
#14 OpenAPI param type "string" (not "String")
#15 no Towarsd typo
#16 keystore password from env var, never a default
Modules added (src/*)
- config/ — AppConfig with load_or_default (--config / XTR_CONFIG / ./xtr.yaml search path)
- dsl/ — XRoadTemplate, ServiceMap, loader::load_all, handlebars::expand (unified single-pass render)
- executor/ — PlainExecutor (system trust), SecurityServerExecutor (mTLS via PKCS12 identity), Executor::dispatch
- translate/ — xml_to_json::translate_soap emits {body, headers} with namespaces preserved, attributes as @-keys, repeats as arrays
- router/ — axum routes + AppState wiring
- openapi.rs — build_spec walks ServiceMap, emits stable OpenAPI 3.1 output
- error.rs — XtrError with IntoResponse
- main.rs — tokio + config load + assemble + serve
Tests (29 pass, 0 fail, 0 ignored)
- 5 loader (walk, missing path, extensions, non-YAML skip, parse error)
- 6 handlebars (allow-list filter, drop non-allowlist, auto context, generate.client shape, generate.uuid validity, single-pass regression guard)
- 8 xml_to_json (body/headers extraction with namespace prefixes, UTF-8 Estonian chars, XML entity refs, repeat → array, attributes → @-keys, empty → null, malformed error, namespaced element names)
- 5 openapi (empty map, one service, "string" type regression, requestBody.required toggle, response schema shape)
- 5 integration (health, /api lists loaded services, end-to-end with mock upstream capturing outbound Content-Type + body, unknown-service 404, params filter)
DSL samples
- DSL/samples/ar/{lihtandmed_v3, detailandmed_v2, ettevottegaSeotudIsikud_v1, tegelikudKasusaajad_v2}.yml
- DSL/samples/xroad/{listMethods, allowedMethods}.yml
Imported verbatim from buerokratt/XTR. Live smoke test loads all six into GET /api as OpenAPI operations.
Docs
- book/src/dsl/format.md — new. DSL format, params allow-list, service field semantics, Handlebars auto-context, response shape, end-to-end example.
- book/src/getting-started/run-locally.md — refreshed with real /health + /api output; shipped-sample invocation recipe.
- book/src/getting-started/automated-tests.md — baseline updated to 29/0/0.
- book/src/SUMMARY.md — new DSL section.
Added — task epic system + follow-ups (earlier this cycle)
docs/DESIGN.md— the domain design derived from a direct read of the original buerokratt/XTR. Documents the JVM XTR's public surface, DSL format, config, request lifecycle, and 17 known bugs. Defines the XTR-on-Rust MVP scope (v0.1.0-rc.2), correctness fixes applied, non-goals, crate layout, roadmap to v1.0. Now includes §2.7 X-Road protocol context — the domain gotchas beyond mechanical translation, each cross-linked to a follow-up task.tasks/backlog/002-implement-mvp-v0.1.0-rc.2.md— next task on the roadmap: implement DESIGN.md §8 (the MVP slice).- Task epic system in
tasks/backlog/epic-*/. Three epics filed after the task 001 review, each with its own README:epic-xroad-protocol-compliance/— 3 open tasks (003, 004, 005: Content-Type, response requestHash verification, explicit protocol version in config).epic-operator-onboarding/— 1 open task (006: X-Road cert acquisition + keystore setup docs).epic-testing-infrastructure/— 2 open tasks (007, 008: mock X-Road Security Server for CI, UTF-8 / Estonian charset round-trip test).
- Empty
mainbranch — orphan commit with a README redirecting todev. Reserved for the futurev1.0.0.
Changed
HANDOFF.md— roadmap section rewritten. Task 001 marked done; task 002 up next. New "Open backlog" table listing top-level tasks + epics.README.mdStatus section now surfaces the domain design.book/src/introduction.mdfirst paragraph points atdocs/DESIGN.md.STANDARDS.md§13 extended — task tracking now allows optional epic subdirectories (tasks/backlog/epic-<slug>/mirrored todone/on completion). New "Linking rule" clause: every commit must reference at least one task file.
Task tracking
- Task 001 (deep-dive) moved from
backlog/todone/with a Landed note.
0.1.0 - 2026-07-28
Initial scaffold. Standards-compliant repo skeleton — no shipped
domain functionality yet. Every rule from Ruuter-on-Rust's
STANDARDS.md applied from day one.
Added
- Rust binary crate (
xtr-on-rust) — placeholdermain.rsthat prints a scaffold notice and exits. MSRV pinned to 1.88. - CI workflows (
.github/workflows/):tests.yml— matrix onubuntu-latest+ubuntu-24.04-arm,cargo fmt --check+cargo clippy --all-targets -- -D warnings+cargo test --release --no-fail-fast.security.yml—cargo audit --deny warnings+cargo deny check allon push/PR/daily cron.publish.yml— multi-arch (linux/amd64+linux/arm64) Docker Hub + GHCR publish on release tag orworkflow_dispatch. Cosign keyless signing, SPDX SBOM, in-toto provenance, Trivy vulnerability scan gates signing, smoke test both platforms. Supports SemVer pre-release tags with maturity-scoped moving tag (:rc,:beta,:alpha).docs.yml— mdBook build + GitHub Pages deploy on push tomain.
- Supply-chain configs:
deny.toml— Apache-2.0-compatible license allow-list, banned wildcards, crates.io-only sources..cargo/audit.toml— empty exceptions stub (mirror any entries here intodeny.toml's[advisories].ignore).
- Hardened container:
Dockerfile— multi-stagerust:1.88-slim→debian:bookworm-slim, non-root uid 1000,tinias PID 1.docker-compose.yml—read_only: true,cap_drop: [ALL],no-new-privileges: true, CPU + memory limits,HEALTHCHECK.
- Documentation scaffold (mdBook at
book/):- Getting Started chapters: Prerequisites → Run it locally → Watch the automated tests pass → What to read next.
- Ops chapter: Docker (with placeholder cosign verify recipe).
- Light-on-white theme (
book/theme/custom.css).
- STANDARDS.md — the reference document capturing every rule
this project inherits. Reusable by any
<Product>-on-Rustsibling. - SECURITY.md — private disclosure recipe, response SLA, supported versions, CI supply-chain posture inventory.
- HANDOFF.md — entry point for the next contributor.
tasks/backlog/001-domain-deep-dive-original-xtr.md— first task on the roadmap: analyse the originalbuerokratt/XTRand define XTR-on-Rust's domain surface.