Skip to content

AtomicAssets Track B: end-to-end Docker stack (Jungle 4) + config segment writer#8

Merged
igorls merged 7 commits into
mainfrom
feat/atomicassets-docker-stack
Jun 6, 2026
Merged

AtomicAssets Track B: end-to-end Docker stack (Jungle 4) + config segment writer#8
igorls merged 7 commits into
mainfrom
feat/atomicassets-docker-stack

Conversation

@igorls
Copy link
Copy Markdown
Member

@igorls igorls commented Jun 6, 2026

Stands up the Rust + WormDB ("v3") AtomicAssets serving lifecycle on Docker, fed by the Jungle 4 testnet, plus the config segment writer. Companion serve PR: igorls/wormdb-domain-atomicassets#1.

What's here

  • deploy/atomicassets/docker-compose.yml (mongodb + aa-loader + wormdb-aa), Dockerfile.tools (Rust: snapshot-load + aa-build), Dockerfile.wormdb + build-wormdb.sh (the 4-repo Zig build → bin/wormdb), entrypoints, wormdb.json, README. Pipeline: snapshot → Mongo (snapshot-load --tables atomic) → .wseg (aa-build) → served by wormdb-server (lightapi + atomicassets domains composed).
  • crates/wseg-buildaa-build now emits the config singleton into a new .wseg table (TABLE_AA_CONFIG=22): encode_config/decode_config carry contract / version / collection_format (reusing the schema-format blob) / supported_tokens. The Zig decoder + /v1/config route are in the companion PR.

Chain source

The existing WSL Jungle 4 node (host.docker.internal:28888 chain API / :28080 SHiP); no nodeos container. State is intentionally dual-homed (Mongo + WormDB) for the proving phase — we diff WormDB against the trusted Mongo tier; "store-once" (blacklist AA deltas) is a later config flip once WormDB is proven.

Validated (jungle4-v8 snapshot, block 269541093)

1552 assets / 188 templates / 48 schemas / 31 collections / config decoded with 0 errors; the served API matches the Mongo oracle. Host ports are bound to loopback (require_auth:false = trusted-LOCAL proving deploy — surfaced by the commit security review and hardened).

Notes

  • build-wormdb.sh mirrors the existing wormdb-server/docker/build-linux.sh flow (unverified Zig download + meshguard HEAD clone) — pinning those is a shared follow-up across both scripts.
  • bin/, snapshot/, .env, *.wseg are gitignored.

Part of Phase 1 of the Track-B AtomicAssets plan.

igorls added 3 commits June 6, 2026 16:11
Stand up the Rust+WormDB ("v3") AtomicAssets serving lifecycle on Docker, fed by the
Jungle 4 testnet: snapshot -> Mongo (snapshot-load --tables atomic) -> .wseg (aa-build)
-> served by wormdb-server (lightapi + atomicassets domains composed).

- deploy/atomicassets/docker-compose.yml: mongodb + aa-loader (one-shot) + wormdb-aa
- Dockerfile.tools (Rust: snapshot-load + aa-build), Dockerfile.wormdb (slim runtime),
  build-wormdb.sh (the 4-repo Zig build, quic=false, into bin/wormdb)
- entrypoints, wormdb.json (trusted-network: require_auth=false), README

Chain source = the existing WSL Jungle 4 node (host.docker.internal:28888/:28080); no
nodeos container. Mongo is the aa-build bridge AND the parallel state oracle (state stays
dual-homed Mongo+WormDB while WormDB is proven; store-once is a later flip).

Validated on the jungle4-v8 snapshot (block 269541093): 1552 assets / 188 templates /
48 schemas / 31 collections decoded with 0 errors; GET /atomicassets/v1/assets?owner=...
returns assets newest-first, byte-matching the Mongo oracle.
Security review of the prior commit flagged the Mongo + WormDB host port mappings as
off-host network exposure (Mongo has no auth; the gateway runs require_auth=false for this
trusted-LOCAL proving deploy). Bind all host port mappings to 127.0.0.1 — inter-service
traffic uses the aa-stack bridge (mongodb:27017 / aa-wormdb:6389) and is unaffected; only
the host-side convenience mappings change. 6389 (WormWire) / 6390 (HTTP) documented inline.

Deferred (matches the existing wormdb-server/docker/build-linux.sh pattern, not new here):
pin the Zig tarball SHA-256 + a meshguard commit in build-wormdb.sh.
aa-build now streams atomicassets-config and writes a sentinel-keyed config record into a new
.wseg table (TABLE_AA_CONFIG=22): encode_config/decode_config carry contract, version,
collection_format (reusing the schema-format blob), and supported_tokens
[(token_contract, symbol, precision)]. AtomicBuilder gains push_config + a finish() emit; the
matching Zig decoder + /atomicassets/v1/config route land in wormdb-domain-atomicassets.

(version is absent from the snapshot's config table — sourced from the tokenconfigs delta — so it
is stored empty for now; a snapshot-load follow-up can fill it.) cargo test -p wseg-build green
(config_round_trips, table_ids_are_unique incl. 22).
Copilot AI review requested due to automatic review settings June 6, 2026 20:52
Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request implements the serialization, deserialization, and integration of the AtomicAssets config singleton (TABLE_AA_CONFIG) into the segment builder, along with Docker deployment scripts and configurations for the end-to-end stack. The review feedback highlights critical improvements for robustness and safety, including preventing serialization desynchronization in encode_config when collection formats or supported tokens exceed length limits, adding bounds checking in decode_config to avoid panics on malformed input, and enhancing build script debuggability by removing silent redirections during package installation.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +384 to +410
pub fn encode_config(
contract: u64,
version: &str,
collection_format: &[(String, String)],
supported_tokens: &[(u64, String, i64)],
) -> Vec<u8> {
let mut o = Vec::new();
o.push(ASSET_VERSION);
pu64(&mut o, contract);
let vb = version.as_bytes();
let vlen = vb.len().min(u16::MAX as usize);
pu16(&mut o, vlen as u16);
o.extend_from_slice(&vb[..vlen]);
let fmt = encode_schema_format(collection_format);
pu16(&mut o, fmt.len().min(u16::MAX as usize) as u16);
o.extend_from_slice(&fmt);
pu16(&mut o, supported_tokens.len() as u16);
for (tc, sym, prec) in supported_tokens {
pu64(&mut o, *tc);
let sb = sym.as_bytes();
let sl = sb.len().min(255);
o.push(sl as u8);
o.extend_from_slice(&sb[..sl]);
o.push(*prec as u8);
}
o
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

There are two potential serialization/deserialization mismatch bugs in encode_config:

  1. The collection_format encoded blob (fmt) has its length prefix capped at u16::MAX, but the entire fmt vector is appended to the output buffer. If fmt.len() > u16::MAX, this will cause the decoder to desynchronize and corrupt subsequent fields.
  2. The supported_tokens length prefix is cast to u16 without capping, but the entire list is serialized. If supported_tokens.len() > u16::MAX, the length prefix will truncate (wrap around), but all tokens will still be written, leading to a decoder desynchronization.

We should cap both the length prefixes and the serialized slices/iterators to u16::MAX.

pub fn encode_config(
    contract: u64,
    version: &str,
    collection_format: &[(String, String)],
    supported_tokens: &[(u64, String, i64)],
) -> Vec<u8> {
    let mut o = Vec::new();
    o.push(ASSET_VERSION);
    pu64(&mut o, contract);
    let vb = version.as_bytes();
    let vlen = vb.len().min(u16::MAX as usize);
    pu16(&mut o, vlen as u16);
    o.extend_from_slice(&vb[..vlen]);
    let fmt = encode_schema_format(collection_format);
    let flen = fmt.len().min(u16::MAX as usize);
    pu16(&mut o, flen as u16);
    o.extend_from_slice(&fmt[..flen]);
    let num_tokens = supported_tokens.len().min(u16::MAX as usize);
    pu16(&mut o, num_tokens as u16);
    for (tc, sym, prec) in supported_tokens.iter().take(num_tokens) {
        pu64(&mut o, *tc);
        let sb = sym.as_bytes();
        let sl = sb.len().min(255);
        o.push(sl as u8);
        o.extend_from_slice(&sb[..sl]);
        o.push(*prec as u8);
    }
    o
}

Comment thread crates/wseg-build/src/aa_binfmt.rs Outdated
Comment on lines +412 to +435
/// Decode the config singleton: `(contract, version, collection_format, supported_tokens)`.
pub fn decode_config(b: &[u8]) -> (u64, String, Vec<(String, String)>, Vec<(u64, String, u8)>) {
let mut p = 1usize;
let contract = gu64(b, &mut p);
let vlen = gu16(b, &mut p) as usize;
let version = String::from_utf8_lossy(&b[p..p + vlen]).into_owned();
p += vlen;
let fmt_len = gu16(b, &mut p) as usize;
let collection_format = decode_schema_format(&b[p..p + fmt_len]);
p += fmt_len;
let n = gu16(b, &mut p) as usize;
let mut tokens = Vec::with_capacity(n);
for _ in 0..n {
let tc = gu64(b, &mut p);
let sl = b[p] as usize;
p += 1;
let sym = String::from_utf8_lossy(&b[p..p + sl]).into_owned();
p += sl;
let prec = b[p];
p += 1;
tokens.push((tc, sym, prec));
}
(contract, version, collection_format, tokens)
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-high high

decode_config does not perform any bounds checking on the input slice b. If the input buffer is malformed, truncated, or corrupted, slicing or indexing out of bounds (e.g., &b[p..p + vlen], b[p]) will cause a panic, leading to a potential Denial of Service (DoS) or crash.

We should make the decoder robust by returning an Option and performing bounds checks before slicing or indexing.

/// Decode the config singleton: `(contract, version, collection_format, supported_tokens)`.
pub fn decode_config(b: &[u8]) -> Option<(u64, String, Vec<(String, String)>, Vec<(u64, String, u8)>)> {
    if b.is_empty() {
        return None;
    }
    let mut p = 1usize;
    if b.len() < p + 8 { return None; }
    let contract = gu64(b, &mut p);
    if b.len() < p + 2 { return None; }
    let vlen = gu16(b, &mut p) as usize;
    if b.len() < p + vlen { return None; }
    let version = String::from_utf8_lossy(&b[p..p + vlen]).into_owned();
    p += vlen;
    if b.len() < p + 2 { return None; }
    let fmt_len = gu16(b, &mut p) as usize;
    if b.len() < p + fmt_len { return None; }
    let collection_format = decode_schema_format(&b[p..p + fmt_len]);
    p += fmt_len;
    if b.len() < p + 2 { return None; }
    let n = gu16(b, &mut p) as usize;
    let mut tokens = Vec::with_capacity(n);
    for _ in 0..n {
        if b.len() < p + 8 { return None; }
        let tc = gu64(b, &mut p);
        if p >= b.len() { return None; }
        let sl = b[p] as usize;
        p += 1;
        if b.len() < p + sl { return None; }
        let sym = String::from_utf8_lossy(&b[p..p + sl]).into_owned();
        p += sl;
        if p >= b.len() { return None; }
        let prec = b[p];
        p += 1;
        tokens.push((tc, sym, prec));
    }
    Some((contract, version, collection_format, tokens))
}

Comment on lines +568 to +580
fn config_round_trips() {
let fmt = vec![
("name".to_string(), "string".to_string()),
("img".to_string(), "ipfs".to_string()),
];
let tokens = vec![(crate::name::encode("eosio.token"), "EOS".to_string(), 4i64)];
let blob = encode_config(crate::name::encode("atomicassets"), "1.2.0", &fmt, &tokens);
let (c, v, f, t) = decode_config(&blob);
assert_eq!(c, crate::name::encode("atomicassets"));
assert_eq!(v, "1.2.0");
assert_eq!(f, fmt);
assert_eq!(t, vec![(crate::name::encode("eosio.token"), "EOS".to_string(), 4u8)]);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Update the test to handle the new Option return type of decode_config.

Suggested change
fn config_round_trips() {
let fmt = vec![
("name".to_string(), "string".to_string()),
("img".to_string(), "ipfs".to_string()),
];
let tokens = vec![(crate::name::encode("eosio.token"), "EOS".to_string(), 4i64)];
let blob = encode_config(crate::name::encode("atomicassets"), "1.2.0", &fmt, &tokens);
let (c, v, f, t) = decode_config(&blob);
assert_eq!(c, crate::name::encode("atomicassets"));
assert_eq!(v, "1.2.0");
assert_eq!(f, fmt);
assert_eq!(t, vec![(crate::name::encode("eosio.token"), "EOS".to_string(), 4u8)]);
}
fn config_round_trips() {
let fmt = vec![
("name".to_string(), "string".to_string()),
("img".to_string(), "ipfs".to_string()),
];
let tokens = vec![(crate::name::encode("eosio.token"), "EOS".to_string(), 4i64)];
let blob = encode_config(crate::name::encode("atomicassets"), "1.2.0", &fmt, &tokens);
let (c, v, f, t) = decode_config(&blob).expect("failed to decode config");
assert_eq!(c, crate::name::encode("atomicassets"));
assert_eq!(v, "1.2.0");
assert_eq!(f, fmt);
assert_eq!(t, vec![(crate::name::encode("eosio.token"), "EOS".to_string(), 4u8)]);
}

Comment thread deploy/atomicassets/build-wormdb.sh Outdated
ZIG_VER=0.16.0
echo "=== tools ==="
apt-get update -qq
apt-get install -y -qq curl xz-utils git build-essential cmake libsodium-dev ca-certificates pkg-config >/dev/null 2>&1
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Redirecting both stdout and stderr of apt-get install to /dev/null (>/dev/null 2>&1) makes it extremely difficult to troubleshoot build failures (e.g., network issues, missing repositories, or package conflicts). Since -qq is already used to keep the output quiet, we should remove the redirection so that any actual errors are printed to the console.

Suggested change
apt-get install -y -qq curl xz-utils git build-essential cmake libsodium-dev ca-certificates pkg-config >/dev/null 2>&1
apt-get install -y -qq curl xz-utils git build-essential cmake libsodium-dev ca-certificates pkg-config

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 89d6c2a136

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +391 to +392
if let Some(cfg) = self.config.take() {
tables.push(Table {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve config during compaction

When a segment that contains the new TABLE_AA_CONFIG is later compacted, aa_live::compact_with rebuilds the output through AtomicBuilder::new() and only replays schemas, templates, and assets; there is no raw config setter/carry-over path, so self.config remains None and this branch omits the config table from the compacted segment. In deployments that use the live overlay/compaction path, /atomicassets/v1/config will work on the initial snapshot-built segment but disappear after the first compaction.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds a runnable Docker-based “Track B” end-to-end stack for serving AtomicAssets from a snapshot-derived .wseg via WormDB, and extends aa-build to emit a config singleton into the segment for /v1/config support.

Changes:

  • Add deploy/atomicassets/ Docker Compose stack (MongoDB + one-shot loader + WormDB server), including build scripts, entrypoints, and documentation for Jungle 4.
  • Extend aa-build / AtomicBuilder to stream an atomicassets-config Mongo collection and write a new TABLE_AA_CONFIG=22 singleton table.
  • Add binary encode/decode support and a unit test for the config singleton blob format.

Reviewed changes

Copilot reviewed 14 out of 14 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
deploy/atomicassets/wormdb.json Adds WormDB server/gateway/auth config used by the runtime container.
deploy/atomicassets/README.md Documents the local proving deployment and workflow (snapshot → Mongo → .wseg → serve).
deploy/atomicassets/entrypoint-server.sh Adds a server entrypoint that validates the segment and starts WormDB with the AtomicAssets segment attached.
deploy/atomicassets/entrypoint-loader.sh Adds a loader entrypoint to snapshot-load into Mongo and run aa-build to produce aa.wseg.
deploy/atomicassets/Dockerfile.wormdb Creates a slim runtime image that ships the prebuilt wormdb binary and configs.
deploy/atomicassets/Dockerfile.tools Builds and packages the Rust tooling (snapshot-load, aa-build) into a tools image.
deploy/atomicassets/docker-compose.yml Defines the end-to-end Compose stack (Mongo + loader + server), loopback-bound ports, and profiles.
deploy/atomicassets/build-wormdb.sh Adds an out-of-band Dockerized Zig build script to produce deploy/atomicassets/bin/wormdb.
deploy/atomicassets/.gitignore Ignores local artifacts (bin/, snapshots, .env, *.wseg).
deploy/atomicassets/.env.example Provides environment defaults for snapshot dir, chain, ports, and Mongo cache sizing.
crates/wseg-build/src/bin/aa_build.rs Streams the new atomicassets-config collection before schemas/templates/assets.
crates/wseg-build/src/aa_tables.rs Introduces TABLE_AA_CONFIG=22 and updates table-ID uniqueness test coverage.
crates/wseg-build/src/aa_builder.rs Captures config from Mongo and writes a sentinel-keyed config singleton table into the segment.
crates/wseg-build/src/aa_binfmt.rs Adds encode_config/decode_config and a round-trip unit test for the config blob.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +397 to +408
let fmt = encode_schema_format(collection_format);
pu16(&mut o, fmt.len().min(u16::MAX as usize) as u16);
o.extend_from_slice(&fmt);
pu16(&mut o, supported_tokens.len() as u16);
for (tc, sym, prec) in supported_tokens {
pu64(&mut o, *tc);
let sb = sym.as_bytes();
let sl = sb.len().min(255);
o.push(sl as u8);
o.extend_from_slice(&sb[..sl]);
o.push(*prec as u8);
}
Comment on lines +16 to +17
BIN="$(find /snap -maxdepth 1 -name 'snapshot-*.bin' | head -1 || true)"
[[ -n "$BIN" ]] || BIN="$(find /snap -maxdepth 1 -name '*.bin' ! -name '*archive*' | head -1 || true)"
Comment on lines +28 to +35
ZIG_VER=0.16.0
echo "=== tools ==="
apt-get update -qq
apt-get install -y -qq curl xz-utils git build-essential cmake libsodium-dev ca-certificates pkg-config >/dev/null 2>&1
echo "=== zig ${ZIG_VER} ==="
cd /opt && curl -fsSL "https://ziglang.org/download/${ZIG_VER}/zig-x86_64-linux-${ZIG_VER}.tar.xz" -o zig.tar.xz && tar xf zig.tar.xz
export PATH="/opt/zig-x86_64-linux-${ZIG_VER}:${PATH}"
zig version
Comment thread deploy/atomicassets/build-wormdb.sh Outdated
Comment on lines +42 to +45
echo "=== meshguard from github (replaces the windows symlink) ==="
rm -rf /work/wormdb/deps/meshguard
git clone -q https://github.com/igorls/meshguard /work/wormdb/deps/meshguard
echo "=== zig build (quic=false) ==="
Comment on lines +390 to +401
// config singleton (one sentinel-keyed entry), if the `atomicassets-config` doc was seen.
if let Some(cfg) = self.config.take() {
tables.push(Table {
table_id: TABLE_AA_CONFIG,
index: vec![IndexEntry {
key: SENTINEL_KEY,
off: 0,
len: cfg.len() as u32,
}],
arena: cfg,
});
}
… (PR review)

Addresses gemini/copilot review on #8:
- aa_binfmt encode_config: cap the length prefix AND the appended bytes together (collection_format
  blob + supported_tokens iteration) so the header can never desync the body. (gemini HIGH / copilot)
- aa_binfmt decode_config: fully bounds-checked, returns Option — a truncated/corrupt blob yields
  None instead of panicking (DoS). Test updated + malformed-input cases added. (gemini SECURITY-HIGH)
- aa_builder: the tiny-segment test now also pushes an atomicassets-config doc, exercising
  push_config → finish() emit. (copilot)
- build-wormdb.sh: stop hiding apt-get errors (-qq stays); SHA-256-pin the Zig tarball + commit-pin
  meshguard, for reproducible, verified builds. (gemini MED / copilot supply-chain)
- entrypoint-loader.sh: deterministic snapshot selection (sort + lexically-newest, warn on multiple)
  instead of `find | head -1`. (copilot)

Deferred (tracked): aa_live::compact_with does not yet carry the config table into a compacted
segment — a follow-up for the live-serving daemon path (the frozen-segment Docker stack is unaffected).
cargo test -p wseg-build green (27).
@igorls
Copy link
Copy Markdown
Member Author

igorls commented Jun 6, 2026

Addressed the gemini / copilot / codex review (pushed 77c71e5):

  • decode_config no bounds checking (gemini SECURITY-HIGH) ✅ now fully bounds-checked and returns Option — a truncated/corrupt blob yields None instead of panicking; added empty/truncated test cases.
  • encode_config header/body desync (gemini HIGH / copilot) ✅ the length prefix and the appended bytes are now capped together (collection_format blob + supported_tokens iteration).
  • config test for the Option return (gemini MED) ✅ updated.
  • builder coverage for config (copilot) ✅ the tiny-segment test now pushes an atomicassets-config doc, exercising push_configfinish() emit.
  • apt-get … >/dev/null 2>&1 hides errors (gemini MED) ✅ removed (-qq stays).
  • supply chain — unverified Zig download + meshguard HEAD (copilot) ✅ the Zig tarball is now SHA-256-pinned and meshguard is commit-pinned (56d9d8d); verified by a clean rebuild.
  • non-deterministic snapshot pick (copilot)entrypoint-loader.sh now sorts and takes the lexically-newest, warning if more than one is present.

Deferred (tracked): aa_live::compact_with doesn't yet carry the config table into a compacted segment (codex P2) — that's the live-serving daemon path; the frozen-segment Docker stack this PR ships is unaffected. Follow-up.

cargo test -p wseg-build green (27).

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: df72934abf

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

/// `atomicassets-config` (singleton) → the config blob served at `/v1/config`.
fn push_config(&mut self, d: &Document) {
let contract = doc_str(d, "contract").map(name::encode).unwrap_or(0);
let version = doc_str(d, "version").unwrap_or("").to_string();
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Avoid emitting a blank config version

In the Docker/bootstrap path added here, aa-build reads atomicassets-config documents created by snapshot-load --tables atomic; I checked crates/snapshot-load/src/atomicassets.rs::map_config, and that producer writes contract, counters, collection_format, supported_tokens, and block_num, but never a version field. As a result every real segment built by this flow falls through to "", so /atomicassets/v1/config serves an empty version even though the binary format and API payload now claim to carry it; this should be sourced from server/contract config or deliberately omitted rather than silently encoding an invalid value.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed — sourced properly rather than emitting a blank.

653c71a: the snapshot-load schema-registry pre-pass now also reads atomicassets:tokenconfigs (the v2 {standard, version} table where the contract version actually lives — not the config row) and stashes it on SchemaRegistry; map_config emits version when present and omits the field on pre-v2 chains that lack tokenconfigs, rather than encoding "". 756ed87 (companion serve PR) makes /config drop the field when empty too.

Validated on Jungle 4: tokenconfigs.version = "1.3.1" → the Mongo config doc and /atomicassets/v1/config now report "1.3.1".

…onfigs (PR review)

/atomicassets/v1/config was serving version:"" because map_config never captured it — the
AtomicAssets `version` lives in the contract's `tokenconfigs` table ({standard, version}), not
the `config` row. The schema-registry pre-pass now also reads `atomicassets:tokenconfigs` and
stashes `version` on SchemaRegistry; map_config emits it when present and OMITS the field entirely
on pre-v2 chains that lack tokenconfigs (rather than encoding an invalid "").

Validated on Jungle 4: tokenconfigs.version = "1.3.1" → the Mongo config doc + the served
/config now report "1.3.1". cargo check/clippy/fmt green.
@igorls igorls merged commit 1801f64 into main Jun 6, 2026
1 check passed
@igorls igorls deleted the feat/atomicassets-docker-stack branch June 6, 2026 22:09
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.

2 participants