Repository navigation
Amber v2.0.0-beta.6
Pre-releaseAmber 2.0.0-beta.6
Amber 2.0.0-beta.6 takes libxml2 out of every Amber V2 application. Until
this release, every V2 app linked libxml2, and with it liblzma, because the
schema layer could parse XML request bodies. XML is now opt-in, and the
opt-in is Mica, a pure-Crystal XML parser, so an application that accepts
XML still links no XML library.
What changed
- XML request bodies are opt-in (#1414). The libxml2-backed
Amber::Schema::Parser::XMLParseris removed. A request sent as
application/xml,text/xmlor any+xmltype is answered with
415 Unsupported Media Typeunless the application registers a parser. ParserRegistry.register_body_parser(#1414) lets an application add
a request body format with one call at boot. A parser that raises answers
the request with400 invalid_request_body.- CI is pinned (#1420): Crystal 1.22.1 tarballs checked by sha256,
GitHub Actions by full commit SHA, the CircleCI image by digest,
dependencies from a committedshard.lockwith--frozen, and a second job
that builds crystal-alpha from an exact commit and runs the suite on it. - Specs pass on Crystal 1.22 (#1418), which rejects the empty request
resource and the malformed multipart fixture some specs used.
Turning XML back on with Mica
Add Mica v0.1.0
(release,
commit 6a4866e) to shard.yml:
dependencies:
mica:
github: amberframework/mica
version: 0.1.0Register it in config/initializers/request_body_parsers.cr, and make sure
config/application.cr loads the initializers (require "./initializers/*";
applications generated by Amber CLI 2.0.6 load config/*.cr only):
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
endList the media types in the action's schema:
class CreateSubscriberSchema < Amber::Schema::Definition
content_type "application/json", "application/xml", "text/xml"
field :email_address, String, required: true, format: "email"
endMica::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"} validate the same way. It refuses a
DTD at the <!DOCTYPE (so no external entity, entity expansion or attribute
default reaches the fields), caps bodies at 1 MiB and nesting at 64 levels,
and charges every step of the read to a work budget.
Measured
A sample Amber V2 + Grant + PostgreSQL application
(examples/zero_link_app), built as a static musl binary with
--static --release --no-debug in the official Crystal 1.22.1 Alpine image
(pinned by digest, linux/arm64), from an empty compiler cache, twice per variant. Before
is Amber 2.0.0-beta.5 with its built-in XML parser; after is this release
with Mica registered. Evidence, scripts and raw logs:
examples/zero_link_app/evidence/.
| before (beta.5) | after (#1414 + Mica) | |
|---|---|---|
| libxml2 objects linked | 108 | 0 |
| liblzma objects linked | 68 | 0 |
| zlib objects linked | 15 | 11 |
| static archives used | 12 | 10 |
defined xml* / lzma_* / zlib symbols |
2,420 / 109 / 46 | 0 / 0 / 46 |
-l flags |
adds -lxml2 -llzma |
neither |
| binary size | 29,600,248 bytes | 26,103,720 bytes (11.8% smaller) |
binary size after strip |
11,169,352 bytes | 10,381,728 bytes (7.1% smaller) |
dynamic libraries needed (readelf -d) |
none (static PIE) | none (static PIE) |
| clean release build, 2 runs | 109.0 s, 119.0 s | 93.0 s, 81.0 s |
Both binaries are static: readelf -d lists no NEEDED entries and there is
no program interpreter. zlib stays linked because Grant
(grant/encryption/compression.cr) and asset_pipeline
(asset_pipeline/static_assets.cr) use the standard library's Compress;
Amber itself does not. The remaining archives are libc, libcrypto, libssl,
libgc, libgmp, libpcre2-8, libyaml, libz and the compiler's libgcc and
libgcc_eh.
Build times are reported, not claimed: in this Docker VM (4 CPUs) the same
beta.5 build ranged from 83 to 133 seconds across five clean builds, so two
runs per variant cannot separate a compile-time change from noise.
The after binary, run in an Alpine image with no libxml2 installed, answered
JSON, application/xml and text/xml bodies with 201 and wrote 3 rows; an
invalid email with 422; malformed XML with 400 and its position; an external
entity and a billion-laughs body with 400 at the <!DOCTYPE (byte 0); and
application/xhtml+xml with 415 (results/after.smoke.txt).
Measured on 2026-11-01 with Amber b9d246ba and Mica 2a4ab10, now
c11b6cc after a history rewrite that left file contents identical. The
sample now pins Mica v0.1.0 (6a4866e; see ../shard.lock).
Upgrade
An application that accepted XML bodies through the schema API either moves
those endpoints to JSON or registers Mica as above. Everything else upgrades
by changing the version:
dependencies:
amber:
github: amberframework/amber
version: 2.0.0-beta.6Release
The tag is v2.0.0-beta.6. The GitHub release for the tag states the tagged
commit hash, its tree hash, and the sha256 of the source tarball attached to
the release (scripts/release_checksums.sh v2.0.0-beta.6 produces all three).
A file cannot carry its own tarball's hash, so they live in the release.
Verify this release
Commit: a61cb0e436d3cffbc1feb381187b03c25e45f743
Tree: aef5979f5a1beec1c825a05ef83d8942583b5e38
Tarball: amber-2.0.0-beta.6.tar.gz (git archive --format=tar.gz --prefix=amber-2.0.0-beta.6/ a61cb0e436d3cffbc1feb381187b03c25e45f743)
Tarball sha256: ec0e01f0da912fbc618782ff7b8e610d3414a4c26a6be8b6e6b335aeba388d24