diff --git a/docs/.dockerignore b/docs/.dockerignore new file mode 100644 index 00000000..7c6b8136 --- /dev/null +++ b/docs/.dockerignore @@ -0,0 +1,4 @@ +# Keep the image tiny — only the Containerfile is needed at build time; +# the docs themselves are bind-mounted at runtime. +* +!Containerfile diff --git a/docs/CLAUDE.md b/docs/CLAUDE.md index 09710633..2ef92be8 100644 --- a/docs/CLAUDE.md +++ b/docs/CLAUDE.md @@ -47,7 +47,16 @@ npx puppeteer browsers install chrome-headless-shell cd /tmp && npm install puppeteer ``` -The Mintlify dev server must be running on port 3000. Use the screenshot script to capture pages: +The Mintlify dev server must be running on port 3000. The simplest way is the containerized CLI bundled with this directory: + +```bash +make -C docs dev # builds the image if needed, serves at :3000 +make -C docs broken-links +``` + +Set `ENGINE=podman` if you don't have Docker. To install the CLI on the host instead, see [Mintlify CLI installation](https://www.mintlify.com/docs/installation) — the npm package is `mint` (run `npm i -g mint`, then `mint dev`). + +Use the screenshot script to capture pages once the server is up: ```bash # Usage: ./scripts/screenshot.js [output-file] [scroll-offset] diff --git a/docs/Containerfile b/docs/Containerfile new file mode 100644 index 00000000..aeaf3614 --- /dev/null +++ b/docs/Containerfile @@ -0,0 +1,22 @@ +# Containerized Mintlify CLI for local docs validation. +# +# Build: make -C docs build-image +# Serve: make -C docs dev # http://localhost:3000 +# +# The container only carries the CLI. The actual docs are bind-mounted from +# this directory at runtime, so edits show up live in the dev server. + +FROM node:20-alpine + +# `mint` shells out to git for some operations; install it. +RUN apk add --no-cache git + +# Install the Mintlify CLI globally. Package name is `mint` (not `mintlify`). +# Bump deliberately by changing the @ pin. +RUN npm install -g mint@latest + +WORKDIR /docs + +EXPOSE 3000 + +CMD ["mint", "dev", "--port", "3000"] diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 00000000..681f80dd --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,38 @@ +# Docs dev tooling — Mintlify in a container. +# +# `make dev` is the common case: builds the image (if needed) and starts +# the local dev server with this directory bind-mounted, so edits show +# up live. + +IMAGE_NAME ?= visualsign-mintlify +PORT ?= 3000 +ENGINE ?= docker + +.PHONY: build-image dev broken-links shell rss + +build-image: + $(ENGINE) build -t $(IMAGE_NAME) -f Containerfile . + +dev: build-image + $(ENGINE) run --rm -it \ + -p $(PORT):3000 \ + -v $(CURDIR):/docs \ + $(IMAGE_NAME) + +broken-links: build-image + $(ENGINE) run --rm \ + -v $(CURDIR):/docs \ + $(IMAGE_NAME) mint broken-links + +# Open an interactive shell in the container for ad-hoc CLI work. +shell: build-image + $(ENGINE) run --rm -it \ + -v $(CURDIR):/docs \ + --entrypoint sh \ + $(IMAGE_NAME) + +# Fetch the auto-generated RSS feeds from a running dev server (port $(PORT)). +# Run `make dev` in another terminal first. +rss: + @echo "Changelog feed:" && curl -sf http://localhost:$(PORT)/changelog/rss.xml | head -40 + @echo "Blog feed:" && curl -sf http://localhost:$(PORT)/blog/rss.xml | head -40 diff --git a/docs/blog.mdx b/docs/blog.mdx new file mode 100644 index 00000000..5d1cfc9b --- /dev/null +++ b/docs/blog.mdx @@ -0,0 +1,10 @@ +--- +title: "Blog" +description: "Concept exploration, design pieces, and deeper dives from the VisualSign team." +--- + +Subscribe via RSS at [`/blog/rss.xml`](/blog/rss.xml). Posts are tagged by chain and theme. Each entry below is a stub — click through for the full essay. + + + Adding a new protocol to VisualSign rarely touches existing code. The recent Solana preset wave is a useful proof point: 14 protocols shipped without changes to existing decoders or the core parser. This post walks through the three composition primitives — the built-in IDL parser, protocol-specific preset overlays, and layered registries — and why each preset is a pure addition rather than a modification. [Read the full post](/blog/2026-05-13-composable-solana-presets). + diff --git a/docs/blog/2026-05-13-composable-solana-presets.mdx b/docs/blog/2026-05-13-composable-solana-presets.mdx new file mode 100644 index 00000000..c47aeb2e --- /dev/null +++ b/docs/blog/2026-05-13-composable-solana-presets.mdx @@ -0,0 +1,61 @@ +--- +title: "Composable by design: how Solana presets stack" +description: "Adding a new protocol to VisualSign rarely touches existing code. Here's how the IDL parser, preset overlays, and a build-time registry compose into one signable payload." +--- + +In the last few weeks, VisualSign gained decoding for Jupiter (Borrow, Earn, Perpetuals, v6 route_v2), Kamino (Borrow, Vault, Farms, Limit Orders), Meteora (DLMM, DAMM V2), MetaDAO (Futarchy, Conditional Vault), Orca Whirlpool, Onre App, Neutral Trade, DFlow Aggregator, and Exponent Finance. Fourteen protocols. Each PR added a single directory under `src/chain_parsers/visualsign-solana/src/presets/` and a handful of fixtures. None of them changed how any of the other presets decode, and none of them touched the core parser. + +That's not luck. It's the consequence of a small, deliberate set of composition primitives. + +## The three pieces + +VisualSign's Solana decoder is built from three layers you can think about independently: + +1. **A built-in IDL parser.** Any Anchor-style program with an IDL gets a typed view of each instruction — discriminator, account list, argument values — produced generically from the IDL alone. This is the `src/idl/` module. It does not know about Jupiter or Kamino. + +2. **Protocol-specific preset overlays.** A preset is a small unit that says "for *this* program id, take the typed view and produce these human-readable fields." Presets implement `InstructionVisualizer` and declare which program id they handle via `SolanaIntegrationConfig`. They never parse bytes; they consume the IDL view and call field builders (`create_text_field`, `create_amount_field`, `create_raw_data_field`) to shape output. Each preset lives in its own directory. + +3. **A build-time registry.** `build.rs` scans `src/presets/` and emits a generated `available_visualizers()` function — one `Box::new(::Visualizer)` per directory. The build comment in the script puts the design intent plainly: "this should allow us to functionally compose instructions at the time of display." There is no central registration site to edit. Adding a directory is the registration. + +Field builders sit underneath all three layers. They enforce shape and determinism, so two presets emitting the same logical content produce byte-identical output. That property is what makes the composed payload safe to hash. + +## What happens when a transaction lands + +A Solana transaction is, in VisualSign's view, a list of instructions. Decoding is a fold: + +```text +for each instruction in the transaction: + visualize_with_any(available_visualizers(), context) + -> first preset whose can_handle() returns true + -> preset emits an AnnotatedPayloadField +collect fields -> SignablePayload +``` + +`available_visualizers()` is the generated slice. `unknown_program` always sorts last, so it catches anything no specific preset claimed. A preset registered for Jupiter's program id has zero knowledge of MetaDAO's preset, and vice versa. The dispatch lookup is the only coupling. + +This is why the preset wave required no changes to the core. Each new directory added: + +- The IDL JSON (or accepted the built-in IDL path). +- One preset `mod.rs` implementing `InstructionVisualizer`. +- One `config.rs` implementing `SolanaIntegrationConfig` with the program id. +- Fixture tests under `tests/fixtures/`. + +Nothing else moved. Reviewers read each preset PR in isolation. CI sees no ripple. + +## What composability buys you + +If you're **adding a new Solana protocol**, the design pushes you toward a particular shape: one IDL, one visualizer, one config, a handful of fixtures, one new directory. The same shape your reviewer expects, the same shape we expect from ourselves. The `solana-add-idl` skill scaffolds this for you when the protocol exposes an on-chain IDL. + +If you're **integrating VisualSign into a wallet**, composability shows up at the data layer too. The Ethereum side uses `LayeredRegistry` to compose wallet-provided overrides on top of compiled-in defaults — wallets can ship their own opinions about token metadata without forking VisualSign. The Solana side currently composes through fixed IDL + preset layers; wallet-side overlays are the natural next step. + +If you're **building a policy engine or signing pipeline on top of VisualSign**, the chain metadata + content digests change in the HTTP gateway (PR #287) gives you a stable input contract. The gateway forwards `chain_metadata` end-to-end and exposes payload digests on every response. Your policy logic composes onto our parser output without re-parsing or re-decoding. + +If you're **writing tests**, surfpool (PR #202) composes real Solana mainnet state into the local harness. Tests run against forked chain data, which catches drift between IDL assumptions and on-chain reality without a live cluster. + +## The seams are the product + +"Composable by design" is a phrase. The work is in keeping the seams clean as surface area grows. Build-time discovery of presets means there's no list to drift out of sync. Field builders mean two contributors emitting the same content produce identical bytes. A typed IDL view between the raw instruction and the preset means presets only see well-shaped input. + +The preset wave is a useful proof point. The more interesting test will be the next chain we add — and watching whether the same primitives carry across. + +To start adding a Solana preset, see [Adding a Solana preset](/chains/solana). To see what shipped recently, see the [Changelog](/changelog). diff --git a/docs/changelog.mdx b/docs/changelog.mdx new file mode 100644 index 00000000..63ef50a9 --- /dev/null +++ b/docs/changelog.mdx @@ -0,0 +1,41 @@ +--- +title: "Changelog" +description: "Curated release notes for VisualSign — one stream per chain plus a core stream, versioned by release." +--- + +Pick the stream that matters to you. Each stream has its own RSS feed. + + + + Solana preset coverage, IDL handling, decoder fixes. RSS at [`/changelog/solana/rss.xml`](/changelog/solana/rss.xml). + + + EVM protocol decoders, ABI registry changes, contract additions. RSS at [`/changelog/ethereum/rss.xml`](/changelog/ethereum/rss.xml). + + + Sui Move package decoders, programmable transactions. RSS at [`/changelog/sui/rss.xml`](/changelog/sui/rss.xml). + + + Tron contract decoders, TRC standards. RSS at [`/changelog/tron/rss.xml`](/changelog/tron/rss.xml). + + + Core types, field builders, HTTP/gRPC API, attestation, policy, build and test infrastructure. RSS at [`/changelog/core/rss.xml`](/changelog/core/rss.xml). + + + +## How releases are tagged + +Tags are auto-cut from `main` by the release workflow. The `description` on each `` block points to the first release that contains the change. If you pull a tag at or above that version, the change is in. + +## How entries are authored + +Contributors drop a draft fragment in `docs/changelog/_drafts/` on the PR that ships the change. A maintainer batches drafts into one or more `` blocks at curation time, splits them across the per-chain and core pages by category, and deletes the consumed drafts. See [`docs/changelog/_drafts/README.md`](https://github.com/anchorageoss/visualsign-parser/blob/main/docs/changelog/_drafts/README.md) for the format. + +## Tag vocabulary + +All streams use a shared controlled tag vocabulary so RSS subscribers can filter: + +- **Audience** — `Wallet API`, `Contributors`, `Security`, `Ecosystem`. +- **Theme** — `Policy`, `Architecture`, `Lints`, `Attestation`, `Performance`. + +Chain pages do not use a chain tag (the page already conveys the chain). diff --git a/docs/changelog/_drafts/README.md b/docs/changelog/_drafts/README.md new file mode 100644 index 00000000..205c7a7b --- /dev/null +++ b/docs/changelog/_drafts/README.md @@ -0,0 +1,62 @@ +# Changelog drafts + +PR authors of user-facing changes drop a fragment here. A maintainer batches drafts into one or more `` blocks on `../changelog.mdx` when a meaningful release ships, then deletes the consumed drafts. + +This directory is intentionally not listed in `docs.json` navigation, so Mintlify does not publish it. + +## Fragment format + +Name the file `YYYY-MM-DD-.md`. Include YAML frontmatter and a short prose body. + +Example (`2026-05-05-metadao-futarchy.md`): + +```markdown +--- +category: solana # one of: solana | ethereum | sui | tron | core +label: "Added MetaDAO Futarchy preset" +description: "" # filled in at curation time with the release version (e.g. "v0.646.0") +tags: ["Wallet API"] +--- +Solana governance transactions now decode conditional vault and market +interactions. See [Solana presets](/chains/solana). +``` + +The maintainer reads the frontmatter into the corresponding props on ``, lifts the body into the block, and writes it to the matching page (`../solana.mdx`, `../ethereum.mdx`, `../sui.mdx`, `../tron.mdx`, or `../core.mdx`). `description` is filled in with the first release tag that contains the change — look up via `git tag --contains --sort=v:refname | head -1`. + +## Category choice + +- **`solana`** — anything under `src/chain_parsers/visualsign-solana/`: presets, IDL handling, decoder fixes, fixture coverage. +- **`ethereum`** — anything under `src/chain_parsers/visualsign-ethereum/`: protocol decoders, ABI registry, contract additions. +- **`sui`** — anything under `src/chain_parsers/visualsign-sui/`: Move package decoders, programmable transactions. +- **`tron`** — anything under `src/chain_parsers/visualsign-tron/`: contract decoders, TRC standards. +- **`core`** — everything else: core types in `src/visualsign`, field builders, the parser binaries (`src/parser/{cli,app,grpc-server}`), the HTTP gateway, attestation, policy, codegen, integration tests, build infrastructure. + +Don't put a chain tag in the `tags` array for per-chain entries — the page already conveys the chain. Reserve tags for audience and theme. + +If a change spans multiple categories (rare — e.g., a core change that requires per-chain updates), drop one fragment per affected category. + +## Controlled tag vocabulary + +Use tags from the shared vocabulary so subscribers can filter: + +- **Audience** — `Wallet API`, `Contributors`, `Security`, `Ecosystem`. +- **Chain** — `Ethereum`, `Solana`, `Sui`, `Tron`. +- **Theme** — `Policy`, `Architecture`, `Lints`, `Attestation`, `Performance`. + +A single fragment usually has one audience tag, one chain tag (if applicable), and zero or one theme tag. + +## What counts as user-facing + +Drop a draft when the change is one of: + +- New chain or preset coverage. +- A change to VisualSign payload shape, field names, or determinism guarantees. +- A new or changed CLI, gRPC, or HTTP surface. +- A security-relevant change (TEE, attestation, policy). +- A breaking change to wallet integration. + +Skip drafts for internal refactors, test changes, dependency bumps, and CI plumbing. + +## Why this workflow + +Authoring the entry close to the change keeps facts accurate. Curating at release time keeps the published changelog readable. Maintainers do not have to mine `git log` for context that has already faded. diff --git a/docs/changelog/core.mdx b/docs/changelog/core.mdx new file mode 100644 index 00000000..bcb482ea --- /dev/null +++ b/docs/changelog/core.mdx @@ -0,0 +1,10 @@ +--- +title: "Changelog — Core" +description: "Core types, field builders, HTTP/gRPC API, attestation, policy, and build/test infrastructure. One entry per meaningful release." +--- + +Subscribe via RSS at [`/changelog/core/rss.xml`](/changelog/core/rss.xml). Filter by tag — `Wallet API`, `Security`, `Architecture`, `Policy`. + + + The HTTP gateway now forwards `chain_metadata` end-to-end and exposes content digests on every response. Downstream tools — policy engines, signing pipelines, audit logs — can compose VisualSign output into their own workflows without re-parsing the transaction. + diff --git a/docs/changelog/ethereum.mdx b/docs/changelog/ethereum.mdx new file mode 100644 index 00000000..2c25fe3e --- /dev/null +++ b/docs/changelog/ethereum.mdx @@ -0,0 +1,8 @@ +--- +title: "Changelog — Ethereum" +description: "Ethereum protocol decoders, ABI registry changes, EVM-specific contract additions, and decoder fixes. One entry per meaningful release." +--- + +Subscribe via RSS at [`/changelog/ethereum/rss.xml`](/changelog/ethereum/rss.xml). The feed populates as new entries land. + +No entries yet. Recent Ethereum work is tracked in [GitHub commits under `src/chain_parsers/visualsign-ethereum/`](https://github.com/anchorageoss/visualsign-parser/commits/main/src/chain_parsers/visualsign-ethereum); curated entries appear here as part of the next meaningful release. diff --git a/docs/changelog/solana.mdx b/docs/changelog/solana.mdx new file mode 100644 index 00000000..7292052b --- /dev/null +++ b/docs/changelog/solana.mdx @@ -0,0 +1,16 @@ +--- +title: "Changelog — Solana" +description: "New Solana preset coverage, IDL handling changes, and Solana-specific decoder fixes. One entry per meaningful release." +--- + +Subscribe via RSS at [`/changelog/solana/rss.xml`](/changelog/solana/rss.xml). Filter by tag — `Wallet API`, `Contributors`, `Policy`, `Architecture`. + + + VisualSign now decodes Jupiter (Borrow, Earn, Perpetuals, v6 route_v2), Kamino (Borrow, Vault, Farms, Limit Orders), Meteora (DLMM, DAMM V2), MetaDAO (Futarchy, Conditional Vault), Orca Whirlpool, Onre App, Neutral Trade, DFlow Aggregator, and Exponent Finance. + + None of these additions required changes to existing decoders or to the core parser. Each preset is a self-contained directory under `src/chain_parsers/visualsign-solana/src/presets/`, auto-registered by `build.rs`. See [Composable by design: how Solana presets stack](/blog/2026-05-13-composable-solana-presets) for the architecture, and [Adding a Solana preset](/chains/solana) for the how-to. + + + + The built-in Solana IDL parser path is now fuzz-tested against real mainnet state via Surfpool. Composing live chain state into the local test harness catches drift between IDL assumptions and on-chain reality, without requiring a live cluster. + diff --git a/docs/changelog/sui.mdx b/docs/changelog/sui.mdx new file mode 100644 index 00000000..e83e2370 --- /dev/null +++ b/docs/changelog/sui.mdx @@ -0,0 +1,8 @@ +--- +title: "Changelog — Sui" +description: "Sui Move package decoders, programmable transaction support, and Sui-specific decoder fixes. One entry per meaningful release." +--- + +Subscribe via RSS at [`/changelog/sui/rss.xml`](/changelog/sui/rss.xml). The feed populates as new entries land. + +No entries yet. Recent Sui work is tracked in [GitHub commits under `src/chain_parsers/visualsign-sui/`](https://github.com/anchorageoss/visualsign-parser/commits/main/src/chain_parsers/visualsign-sui); curated entries appear here as part of the next meaningful release. diff --git a/docs/changelog/tron.mdx b/docs/changelog/tron.mdx new file mode 100644 index 00000000..bf6acf78 --- /dev/null +++ b/docs/changelog/tron.mdx @@ -0,0 +1,8 @@ +--- +title: "Changelog — Tron" +description: "Tron contract decoders, TRC standards support, and Tron-specific decoder fixes. One entry per meaningful release." +--- + +Subscribe via RSS at [`/changelog/tron/rss.xml`](/changelog/tron/rss.xml). The feed populates as new entries land. + +No entries yet. Recent Tron work is tracked in [GitHub commits under `src/chain_parsers/visualsign-tron/`](https://github.com/anchorageoss/visualsign-parser/commits/main/src/chain_parsers/visualsign-tron); curated entries appear here as part of the next meaningful release. diff --git a/docs/docs.json b/docs/docs.json index 4d3c4f95..2565f546 100644 --- a/docs/docs.json +++ b/docs/docs.json @@ -129,6 +129,44 @@ ] } ] + }, + { + "tab": "Changelog", + "groups": [ + { + "group": "Overview", + "pages": [ + "changelog" + ] + }, + { + "group": "Chain streams", + "pages": [ + "changelog/solana", + "changelog/ethereum", + "changelog/sui", + "changelog/tron" + ] + }, + { + "group": "Core", + "pages": [ + "changelog/core" + ] + } + ] + }, + { + "tab": "Blog", + "groups": [ + { + "group": "Blog", + "pages": [ + "blog", + "blog/2026-05-13-composable-solana-presets" + ] + } + ] } ] },