Welcome, agent. This site sets exactly one cookie — a security escort that keeps your session yours. No tracking, no ads, and no decision to make: it's required to protect you, so this is a heads-up, not a permission slip. Browse freely either way.
The full briefing, if you're curious
The cookie's sole assignment is CSRF protection — keeping your session yours and no one else's. It is technically necessary, set automatically, and exempt from consent under the ePrivacy Directive. Surveillance isn't our style — securing things is.
One more thing: if you send us an inquiry, your message is processed and automatically scored so the right response reaches you faster. That's the whole file — the long-form dossier lives in the Privacy Policy.
We counted the C libraries a plain Amber V2 web application carries into production and found libxml2 in every one, used or not. Amber 2.0.0-beta.6 takes it out, and Mica, a pure-Crystal parser, brings XML back for the applications that need it.
We wanted to know how many outside C libraries a plain Amber V2 web application carries into production, and whether each one earns its place. The first answer was a surprise: every Amber V2 application linked libxml2, the C library that parses XML, along with liblzma, the compression library it brings, whether or not the application ever read an XML document.
That mattered more than the bytes. Amber's schema layer would hand any request body sent as XML to libxml2, so any client on the internet could ask the server to parse a document of its choosing. libxml2's own README says it is not recommended for untrusted input, and its sole maintainer stepped down in 2025. A web framework is about the most untrusted input a parser will ever see.
Amber 2.0.0-beta.6 takes libxml2 out. XML becomes something an application turns on, and when it does, it uses Mica, a pure-Crystal XML parser from the same family of projects, so the binary still links no XML library.
What changed in Amber
Amber V2 parses JSON, URL-encoded and multipart forms, CBOR and COSE request bodies on its own. A body sent as application/xml, text/xml or any +xml type is now answered with 415, the HTTP status for a format the server does not accept. An application that wants XML registers a parser once at boot with register_body_parser, and the action's schema lists the media type as before.
# config/initializers/request_body_parsers.cr
require "mica/xml/fields"
{"application/xml", "text/xml"}.each do |media_type|
Amber::Schema::Parser::ParserRegistry.register_body_parser(media_type) do |body|
Mica::XML::Fields.read(body)
end
end
Mica::XML::Fields.read turns an XML body into the same fields a JSON object gives, so <subscriber><email_address>ada@example.com</email_address></subscriber> and {"email_address": "ada@example.com"} pass through one schema the same way. It refuses a document type declaration at the first <!DOCTYPE, which closes the door on external entities and entity expansion tricks, caps a body at 1 MiB and nesting at 64 levels, and charges every step of the read to a work budget, so a hostile body costs time in proportion to its size and no more.
What we measured
We built the same small application twice: an Amber V2 app with a Grant model, a PostgreSQL table and one API action that accepts JSON or XML. The first build used Amber 2.0.0-beta.5, which parsed XML with libxml2; the second used beta.6 with Mica registered. Both were static musl binaries built from an empty compiler cache in the official Crystal 1.22.1 Alpine image, and the linker wrote a map of every piece of code it took from each library.
Same app, static build
beta.5 libxml2, liblzma objects
176
beta.6 libxml2, liblzma objects
0
beta.5 binary size
29.6 MB
beta.6 binary size
26.1 MB
beta.5 binary size, stripped
11.2 MB
beta.6 binary size, stripped
10.4 MB
The beta.5 build takes 176 objects from libxml2 and liblzma; the beta.6 build takes none, and the binary is 3.5 MB smaller (0.8 MB once stripped). Measured 2026-11-01 on the sample app in the Amber repository, two builds per version.
The new binary links no XML library at all, and it still accepts XML: run in an image with no libxml2 installed, it stored a subscriber sent as XML exactly as it stored one sent as JSON, and it refused the classic XML attacks (an external entity, a billion-laughs expansion) at the first line of the body. Two honest notes. zlib is still linked, because Grant and the asset pipeline compress data with it; that one is on our list. And build times moved from 109 and 119 seconds to 93 and 81, but the same beta.5 build ranged from 83 to 133 seconds on this machine, so we are not claiming a compile-time gain from it.
Why we wrote Mica
Mica exists because we want the Amber ecosystem to rely on pieces it owns: parsers written against the published standards, carrying the lessons of real security reports. Her XML parser was built against the 2024 REXML CVEs as test cases. It passes 1,914 of the 1,915 W3C XML 1.0 conformance cases and all 237 scored XML 1.1 cases, ran 2,000,000 fuzz cases with no crash, and proves linear running time on 67 attack patterns. The request-body reader added for this release went through eight rounds of independent review by OpenAI's Codex; every finding was fixed before the next round, and the eighth found nothing left to fix.
Try it
Point an application at the new beta and, if it accepts XML, add Mica and the initializer above. The release notes walk through the upgrade, and the sample application, its build scripts and the raw measurements are in the Amber repository under examples/zero_link_app. Mica 0.1.0 has its own release notes.
libxml2 is the first of the C libraries we plan to retire from Amber V2, one release at a time, each with its own measurement. If your own product carries a dependency you would rather not have to answer for, that is the kind of question we enjoy working through with clients: talk to AgentC. The quickest library to audit, it turns out, is the one you no longer link.