Skip to content

DIG-Network/dig-store

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

581 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

dig-store

A Git-shaped, encrypted, content-addressable store that compiles to a single self-defending WebAssembly module.

CI Release License: GPL-2.0 Platforms Rust 1.94.1


dig-store gives you Git-style commands — init, add, commit, log, clone, push, pull — for a store that is encrypted at rest and compiles into a single .wasm file. That one file is both your data and the server that gates access to it. A host that stores or relays it sees only ciphertext addressed by hashes; it cannot read what it carries.

You address content with a URN, and the URN is the key: it both locates and decrypts. Hand someone a URN and they can read that resource; without it they can't, and there's no separate password or access list to manage.

Unlike Git, dig-store is built for build output, not repo source — you point a store at a directory like dist/ and it captures what's there.

New here? The full design is in the whitepaper: docs/whitepaper/digstore-whitepaper.pdf.


Install

Universal installer (recommended)

The DIG installer downloads the right dig-store binary for your OS and adds it to your PATH. It lives in its own repo, DIG-Network/dig-installer (the GUI desktop installer — the single-file DigStore-Setup-* — lives there too, and it can optionally also install the dig-node local node).

# macOS / Linux
curl -fsSL https://raw.githubusercontent.com/DIG-Network/dig-installer/main/install.sh | sh
# Windows (PowerShell)
irm https://raw.githubusercontent.com/DIG-Network/dig-installer/main/install.ps1 | iex

Then open a new terminal and check it works:

dig-store --version

Already installed? Keep it current with the built-in self-updater:

dig-store update          # download + install the latest release, in place
dig-store update --check  # just report whether a newer release exists

Raw binary (macOS / Linux)

Prefer the installer above. If you grab the raw per-OS binary from the Releases page instead, pick the file for your CPU — dig-store-<ver>-macos-arm64 (Apple Silicon: M1/M2/M3) or dig-store-<ver>-macos-x64 (Intel Mac); dig-store-<ver>-linux-x64 (or the aarch64-unknown-linux-gnu.tar.gz for ARM Linux). A macOS/Linux binary has no file extension — that's normal, not a broken download.

Downloaded binaries lose their exec bit, and macOS quarantines an unsigned download (Gatekeeper). Two commands fix both — no sudo needed:

chmod +x dig-store-<ver>-macos-arm64                 # make it executable
xattr -d com.apple.quarantine dig-store-<ver>-macos-arm64   # macOS only: clear Gatekeeper

# then put it on your PATH under the name `dig-store` (Apple Silicon: no sudo):
mv dig-store-<ver>-macos-arm64 /opt/homebrew/bin/dig-store   # Intel mac / Linux: /usr/local/bin
dig-store --version

If you hit permission denied, it's the exec bit or quarantine above — not privileges; sudo is the wrong fix.

Build from source (any platform)

You need Rust (pinned to 1.94.1 via rust-toolchain.toml). The CLI embeds a WebAssembly guest, so build that first:

rustup target add wasm32-unknown-unknown
cargo build -p digstore-guest --target wasm32-unknown-unknown --release
cargo build -p digstore-cli --release

The binary is at target/release/dig-store (dig-store.exe on Windows). Copy it somewhere on your PATH.


Quick start

Start free — scaffold a store from a template and preview it over the real chia:// read path locally, with no wallet, no chain, and nothing spent. Only init/deploy touch mainnet (and cost $DIG).

dig-store new static-site my-store   # scaffold a working store — FREE, no wallet/chain
cd my-store
dig-store dev                        # live local preview over the real chia:// read path — FREE

When it's ready, publish it on Chia. This is the canonical publish flow — run it top-to-bottom from inside your project (it spends the dynamic per-capsule $DIG price + a small XCH fee, fetched live so you always pay the same as DIGHub):

dig-store init                       # create the on-chain store (mints on Chia; store id = launcher id)
dig-store add -A                     # stage every file under the store root
dig-store add --discovery            # publish the public /.well-known discovery manifest
dig-store commit -m "v3"             # anchor a new capsule on-chain (dynamic per-capsule $DIG price + XCH fee)
dig-store push origin                # push the deployment to DIGHub (rpc.dig.net)

Then inspect and read it back:

dig-store log                        # list published capsules (each root hash = one capsule)
dig-store urn index.html             # preview the URN a file will have — no guessing

# read a resource back (store id + root come from `dig-store log --json`):
dig-store cat urn:dig:chia:<storeID>:<rootHash>/index.html

Prefer JS? npm create dig-app scaffolds the same static-site template (and more) from Node — the same free, no-spend starting point.

Commands discover the .dig/ workspace by walking up from wherever you are (like Git). add/urn operate on the store's content root (the current directory by default; commonly a build dir — see below), and resource keys are always relative to that root, so URNs are stable no matter which subdirectory you run from.


Multiple stores per workspace

A single .dig/ workspace can hold many stores, each with its own content, keys, and history (a store accrues a series of capsules as you commit).

dig-store init site --dir dist      # a store named "site" that captures ./dist
dig-store init docs --dir build/docs
dig-store stores                    # list stores; * marks the active one + capacity
dig-store use site                  # switch the active store

dig-store --store site add -A       # stage everything under dist/ into "site"
dig-store staged                    # what's staged + size + remaining headroom
dig-store unstage                   # clear staging
dig-store commit -m "v1"            # seal a deployment; writes a local urns.json index
  • Store selection: --store <name> > the active store (use) > the single store if there's only one. (--project is a hidden, deprecated alias of --store.)
  • Content root: each store captures a build directory (default: the current dir; set with --dir at init or dig-store dir <path>). -C/--cwd <path> overrides it for one command.
  • Per-store cap: each store is capped at 128 MB of staged content, enforced at add (and defensively at commit); remaining capacity is shown by add, status, staged, and dig-store stores.
  • URN manifest: commit writes a local urns.json / urns.txt — the publisher's index of every shareable URN for that deployment.

How content is addressed: URNs

Every resource is named by a URN. The URN alone locates and decrypts it:

urn:dig:<chain>:<storeID>[:<rootHash>][/<resourceKey>]
Part Meaning
<chain> Chain identifier, e.g. chia
<storeID> Your 64-hex store id (required)
<rootHash> Optional: pin a specific deployment root; omit for the current one
<resourceKey> Optional: which resource (content-root-relative path)

dig-store urn [PATHS] previews the exact URN (and retrieval key) a file will have against the active store — so you can check before you commit instead of guessing.


Public vs private stores

dig-store init             # public:  anyone with the URN can read
dig-store init --private   # private: URN locates, but reading also needs a secret salt
  • Public — the URN is sufficient to decrypt.
  • Private — decryption also requires a secret salt the publisher holds and shares out-of-band. Pass it with --salt <hex> on cat/checkout.

Sharing over a remote

A remote is an HTTPS endpoint that hosts and serves your .wasm module.

# publisher
dig-store remote add origin https://example.com/stores/<storeID>
dig-store push origin

# consumer (fresh directory)
dig-store clone https://example.com/stores/<storeID>
dig-store cat   urn:dig:chia:<storeID>:<rootHash>/readme
dig-store pull  origin          # later: fetch the publisher's newer deployment

clone/pull verify what they download before installing it: the module must match the store id you asked for, and the served root must carry the publisher's signature. A malicious or broken server cannot feed you fabricated content — the command fails instead. Remotes must be https:// (plain http:// is allowed only for localhost).


Deploy from GitHub Actions (CI)

Auto-publish your built site/dapp to your existing store on every push — a new capsule, git-push-to-deploy. The store must already exist (you ran dig-store init once); CI only advances it (it never mints).

One-time setup, on the machine that created the store:

dig-store log --json          # copy the store_id
dig-store deploy-key export   # copy the 64-hex publisher deploy key

Add two repository secretsDIG_MNEMONIC (your funded deploy wallet) and DIG_DEPLOY_KEY (the key above) — commit a dig.toml (see examples/dig.toml), then add the workflow (examples/github-actions-deploy.yml):

- name: Deploy to DIG
  uses: DIG-Network/dig-store@v0.5.29   # pin to a release tag
  with:
    mnemonic: ${{ secrets.DIG_MNEMONIC }}
    deploy-key: ${{ secrets.DIG_DEPLOY_KEY }}
    output-dir: dist

⚠ Security: v1 ships the funded wallet mnemonic into CI as a secret — it can spend ALL of that wallet's DIG/XCH. Use a dedicated, low-balance deploy wallet funded with only enough $DIG for your expected deploys (each deploy costs a uniform per-capsule price in $DIG + a small XCH fee). For the on-chain root advance you can instead use a revocable writer deploy token (see Writer deploy tokens below) so the owner key never enters CI; the funded wallet is still needed to pay the DIG + XCH fee.

dig-store deploy reconstructs the store locally from the deploy key + the on-chain root, stages your output-dir, advances the root, and pushes the new capsule — all non-interactively. See dig-store deploy --help.

Preview a build without spending (free)

dig-store deploy --preview builds a free preview capsule — it runs the real compile → verify → decrypt read path on your output-dir, writes a local .dig artifact, and prints its content address (storeId:rootHash + dig:// URN). No chain, no wallet, no deploy key, nothing spent — the preview store id is a fresh ephemeral id, so a preview never touches (or impersonates) your real store. Use it to verify a build, or to serve a shareable preview from CI:

dig-store deploy --preview                       # → <output-dir>/../.dig-preview/<root>.dig
dig-store deploy --preview --preview-out p.dig   # explicit artifact path

Writer deploy tokens — advance the root without the owner seed

The CI flow above ships the funded wallet into CI. To advance a store's root from CI without exposing the owner key, use a writer deploy token: a revocable delegate the owner pre-authorizes (the hub Teams "Deployer" flow / on-chain updateStoreOwnership). A writer can change only the metadata root — it can never change ownership or melt the store, and the owner revokes it at any time.

dig-store commit -m "deploy" --writer-key $DIGSTORE_WRITER_KEY   # writer-signed root advance
dig-store deploy --writer-key $DIGSTORE_WRITER_KEY               # same, in the CI deploy flow

Prefer the DIGSTORE_WRITER_KEY env var so the key isn't visible in the process table. The wallet seed still pays the per-capsule $DIG price + XCH fee; the writer key only authorizes the on-chain root advance. (This is distinct from the §21 publisher --deploy-key/DIGSTORE_DEPLOY_KEY above, which lets DIGHUb accept the capsule.)


On-chain anchoring (Chia mainnet)

Every store is anchored on Chia mainnet. dig-store init mints an empty store singleton on-chain, and the singleton's launcher id becomes the store id. Every dig-store commit then pushes the new deployment's root to that singleton with an on-chain update and blocks until the update confirms before finalizing the deployment locally.

This spends real XCH and DIG. Anchoring is mandatory — there is no offline mode. init and commit will not proceed without an unlocked wallet seed and enough funds, and they block on mainnet confirmation. All broadcast and chain reads go through coinset.org over HTTPS (no peer node or TLS cert to run).

$DIG fee (per capsule): every init and every commit/deploy pays a $DIG fee to the DIG treasury — embedded atomically in the same spend bundle as the mint/update (memo = store id). The price is dynamic and USD-pegged (≈ $1/capsule/year of hosting ÷ the live DIG price), uniform per capsule; the hub computes the live amount in the browser. The CLI stays deterministic — it never fetches a price itself: it takes the amount as input and falls back to a protocol default if unset. Set it explicitly with --dig-amount <DIG> (e.g. --dig-amount 87.5), the DIGSTORE_DIG_AMOUNT env var, or dig-amount in dig.toml (precedence: flag > env > dig.toml > default). Before submitting, each command prints the cost and your current balance; if the wallet is short on XCH or $DIG the command blocks and tells you what's missing. Need $DIG? Get it on TibetSwap, dexie.space, or xch.9mm.pro. Use dig-store balance to check your spendable XCH (mojos) and DIG at any time.

1. Set up a wallet seed

dig-store keeps an encrypted BIP-39 seed in ~/.dig/seed.enc.

dig-store seed generate          # create a new mnemonic (shown once — back it up)
# or
dig-store seed import            # import an existing mnemonic
dig-store seed status            # is a seed present / unlocked?
dig-store lock                   # clear the cached-unlock session

The seed is encrypted with a passphrase (Argon2id + AES-256-GCM). After unlock it is cached for a configurable TTL; DIGSTORE_PASSPHRASE supplies the passphrase non-interactively (for CI/scripts). Global settings live in ~/.dig/config.toml (coinset_url, unlock_ttl, fee).

2. Fund the wallet

Minting and updates cost both XCH (the transaction fee) and DIG (the DIG token, a Chia CAT). The wallet derived from your seed needs both. Run dig-store balance to see your current spendable XCH (mojos), DIG (3-decimal display), and the wallet receive address. If either is short, init/commit block, disclose the exact cost up front, and print the receive address to fund:

insufficient funds: need <N> mojos, have <M>; fund xch1…

Both XCH and DIG are received at the same xch1… receive address (DIG as a CAT). Send funds there, wait for them to confirm, then retry.

Where to get $DIG: TibetSwap, dexie.space, or xch.9mm.pro — the DIG CAT is a406d3a9…832f81. Get XCH from any Chia exchange or wallet.

3. Init mints, commit anchors

dig-store init                   # mints the store singleton; store id = launcher id
                                # blocks until the mint confirms on mainnet

dig-store add readme.txt --key readme
dig-store commit -m "first deployment"
                                # pushes the new root on-chain; blocks until
                                # confirmed, then finalizes the deployment locally

Both commands take --wait-timeout <secs> (default 300) for how long to wait on confirmation. On a confirm-timeout the store is kept pending (and the local deployment is not finalized) — it is resumable, not lost.

4. Resume / inspect an anchor

dig-store anchor                 # resume a pending anchor: confirm the chain coin
                                # and flip the store to confirmed (idempotent)
dig-store anchor status          # read-only: show the store's anchor state
dig-store anchor status --json   # machine-readable state

Per-store anchor state (network, store id / launcher, coin id, status, last root, last tx id, confirmed height) is recorded in the store's anchor.toml.

The compiled .dig module also embeds the on-chain pointer (network, launcher/store id, current coin id, confirmed height, and a coinset endpoint hint) directly in its data section. dig-store anchor status surfaces this alongside the local anchor.toml state (use --json for machine-readable output); dig-store anchor inspect <module.dig> dumps the pointer from any module file without a local workspace. The embedded coinset URL is a hint only — local config and flags always take precedence.

Note: clone/pull verify the publisher's signature over the served head and verify that the served root equals the store's current on-chain singleton root — read from the chain via the launcher id embedded in the module. They fail closed on a mismatch or an unreachable chain, making the chain the authority for the current root. (A module with no embedded on-chain pointer falls back to the head-signature gate.) See SECURITY.md.


Command reference

Command What it does
dig-store init [name] [--dir <path>] [--private] [--wait-timeout <s>] Create a store (default name default); mints its singleton on mainnet (store id = launcher id); --dir sets its content root
dig-store stores List stores with active marker, root, content root, capacity (the projects alias is hidden/deprecated)
dig-store use <name> Set the active store
dig-store dir [<path>] Show or set the active store's content root
dig-store add <path…> [-A] [--key <name>] Stage files (-A = the whole content root)
dig-store staged / dig-store unstage List the staging area / clear it
dig-store commit [-m <msg>] [--wait-timeout <s>] [--writer-key <writer-seed>] Seal a new deployment, anchor its root on mainnet (blocks until confirmed), compile the module, write the URN manifest. --writer-key advances the root with a revocable writer deploy token instead of the owner seed (the deprecated --deploy-key alias still works)
dig-store status Show staged/modified/untracked + capacity
dig-store log [--limit N] / dig-store diff <a> <b> List / compare deployments
dig-store urn [PATHS…] [--root <hex>] Preview the URN(s) files will have
dig-store cat <urn> [--salt <hex>] [--verify-proof] Read a resource by URN
dig-store checkout <root> --out <dir> [--salt <hex>] Write a whole deployment to a directory
dig-store remote add|list|remove … Manage remotes
dig-store clone <url> / push [remote] / pull [remote] Sync with a remote (verified)
dig-store deploy [--store-id <hex>] [--output-dir <dir>] [--build-command <cmd>] [-m <msg>] [--writer-key <seed>] CI auto-deploy: advance an EXISTING store from a fresh checkout (reads dig.toml); never mints. --writer-key advances the root with a revocable writer deploy token (owner seed stays out of CI)
dig-store deploy --preview [--preview-out <file>] Build a free preview capsule via the real read path (writes a local .dig artifact + content address); no chain, no wallet, nothing spent
dig-store deploy-key export [--out <file>] Export the store's publisher deploy key (for a CI secret)
dig-store anchor [--wait-timeout <s>] Resume a pending on-chain anchor (confirm the coin, flip to confirmed)
dig-store anchor status [--json] Show the active store's anchor state + embedded module chain pointer (read-only)
dig-store anchor inspect <module.dig> [--json] Dump the on-chain pointer embedded in any module file (read-only, no workspace needed)
dig-store balance [--json] Show spendable XCH (mojos) and DIG (3-decimal) + the wallet receive address (read-only)
dig-store seed generate|import|status / dig-store lock Manage the encrypted wallet seed used for anchoring

Global flags: --store <name> (target a specific store), -C/--cwd <path> (operating directory for this command), --dig-dir <path> (workspace location), --json (machine-readable), --quiet, --verbose, --color <auto\|always\|never>.

Agent-friendly surface (scripting / CI)

dig-store is built to be driven by scripts and agents as well as humans:

  • --json — every command emits ONE structured object to stdout (human prose goes to stderr). On failure, --json emits a structured error envelope to stdout instead of prose: {"ok":false,"error":{"code","exit_code","message","hint"}} — branch on error.code (a stable UPPER_SNAKE string) or the exit code, never on the message text.
  • dig-store --help-json — the whole invocation contract as one JSON object: the command tree, the global flags, each arg's choices/default/value_name, and the exit-code table below. One call yields everything needed to invoke the CLI.
  • dig-store completion <bash|zsh|fish|powershell|elvish> — shell completions.

Exit codes

Every failure maps to a distinct, stable exit code (and the matching code in the --json envelope), so a script can tell failure classes apart:

Code code Meaning
0 OK success
1 ERROR an unclassified error
2 INVALID_ARGUMENT a bad/missing argument or flag value
3 NO_STORE no dig-store workspace/store found here
4 NOT_FOUND the requested resource/root/key was not found
5 VERIFICATION_FAILED content failed verification (tamper, wrong salt/key)
6 NETWORK a network/remote error
7 NON_FAST_FORWARD the remote root advanced; pull before pushing
8 UNAUTHORIZED missing/invalid credentials or signing key
9 NO_SEED no wallet seed is set up
10 BAD_PASSPHRASE wrong seed passphrase
11 INVALID_MNEMONIC the BIP-39 mnemonic is invalid
12 INSUFFICIENT_FUNDS not enough XCH or DIG to complete the spend
13 CHAIN a Chia chain / coinset.org error
14 CONFIRM_TIMEOUT on-chain confirmation timed out (resumable)
15 MINT_FAILED the on-chain mint failed
16 UPDATE_FAILED the on-chain root update failed
17 TOO_LARGE the operation is too large for one transaction; split it into smaller batches
18 NEEDS_CONSOLIDATION the wallet is spendable but too fragmented; consolidate coins (or pass --consolidate) first

dig-store --help-json emits this same table under exit_codes (generated from the source, so it never drifts).

Wallet seed

dig-store seed generate|import|status and dig-store lock manage the encrypted BIP-39 wallet seed used for on-chain anchoring — see On-chain anchoring above for details.


What this gives you

  • Encrypted at rest. Content is encrypted with a key derived from its URN. There is no key stored anywhere to recover — lose the URN, lose the read.
  • Provider-blind hosting. Whoever hosts your store holds only ciphertext keyed by hashes; they can't scan it or read requests.
  • Verified downloads. clone/pull reject content that isn't the genuine, publisher-signed store.
  • Uniform, self-contained. A store is a single .wasm, padded to a uniform size so its bytes reveal nothing about how much content it holds. Copy it to back it up; run it to serve it.

Security

Security posture, the hardening applied, and known residual risks are documented in SECURITY.md. Please report vulnerabilities privately to the maintainer rather than opening a public issue.

Help & community

Contributing

Build, test, and contribution guidelines are in CONTRIBUTING.md.

License

Licensed under the GNU General Public License v2.0 — the same license as Git.

About

A Git-shaped, encrypted, content-addressable store that compiles to a single self-defending WebAssembly module.

Topics

Resources

License

Contributing

Security policy

Stars

5 stars

Watchers

0 watching

Forks

Packages

 
 
 

Contributors

Languages