@unconfirmed/publish
v0.2.1
Published
Compose and publish immutable Sui Move packages, with an Onara-sponsored CLI.
Maintainers
Readme
publish
An SDK for composing immutable Sui Move package publishes, plus a thin CLI that publishes one through five packages atomically with a fresh in-memory signer and Onara-sponsored gas.
publish . --network testnet --onara-url <URL> --dry-run
publish . --network testnet --onara-url <URL>
publish package-a package-b --network testnet --onara-url <URL>
publish . --network mainnet --onara-url <URL> --dry-run
publish . --network mainnet --onara-url <URL> --yesEvery successful publish is one atomic programmable transaction:
Publish(modules, dependencies) -> UpgradeCap
0x2::package::make_immutable(UpgradeCap)If either command fails, neither takes effect. The Ed25519 key is generated only after compilation, kept in process memory, used to sign that transaction, and never printed or written to disk. Onara pays gas from its address balance.
SDK
@unconfirmed/publish exposes transaction composition separately from build,
signing, gas selection, execution, and deployment metadata:
import { Transaction } from '@mysten/sui/transactions'
import { addImmutablePublish } from '@unconfirmed/publish'
const transaction = new Transaction()
addImmutablePublish(transaction, artifact)
// The caller chooses the sender, gas strategy, signer, and executor.For independent package batches, createImmutablePublishTransaction(artifacts)
creates the complete PTB. It accepts one through five artifacts, matching Sui's
publish-command limit, and adds one make_immutable call immediately after each
publish. Neither transaction helper sets sender or gas data.
The root export also provides:
buildMovePackageandparseBuildOutputfor argv-safe stock Sui builds;OnaraHttpClientandcreateBoundedFetchfor sponsored execution;extractExecutionResultandextractPublishedPackageIdsfor execution-effect normalization, including all package IDs from a batch;transactionStatusandsponsorshipErrorfor digest-based reconciliation;updatePublishedFileandupdatePublishedTextfor explicit, atomic deployment metadata updates; andpublishPackagesas the one-to-five-package ephemeral-signer batch workflow used by the CLI, pluspublishPackagefor compatibility with one-package SDK callers.
The core publishing options contain network and execution inputs only. CLI
presentation, confirmation, and metadata-write switches are intentionally not
part of the SDK contract. Published.toml is never changed by transaction
composition or effect parsing; callers invoke the metadata helper explicitly
after an applied publish.
Mutation receipts and errors use effect: "applied" | "not_applied" |
"unknown". An unknown outcome carries the locally derived digest whenever one
is available and must be reconciled before retrying.
Why the CLI builds the package
The package directory—not prebuilt bytecode—is the primary input. publish
invokes the installed stock Sui CLI as an argv-safe child process:
sui move build \
--path <PACKAGE_PATH> \
--dump-bytecode-as-base64 \
--build-env <NETWORK>This is preferable to accepting an arbitrary bytecode file by default:
- the bytecode and transitive dependency IDs come from the same build;
- the chosen network is used for both compilation and submission;
- already-published root packages are rebuilt at
0x0, so republishing does not require deleting or editingPublished.tomlfirst; - compiler failures stop before a key is generated or Onara is contacted; and
- the exact Sui CLI version and package digest are recorded in the result.
The CLI creates a temporary, empty Sui client configuration pointed at the
selected RPC. It never uses an ambient Sui address or keystore. The compiler
still uses the package's normal Move.lock, dependency cache, and build/
directory.
After a successful transaction, the selected network section in each package's
Published.toml is updated atomically. Other networks and surrounding comments
are preserved. New records intentionally have no upgrade-capability field.
Use --no-write-published for read-only checkouts.
Install
Requirements:
- Node.js 22 or newer (Bun also works);
- the
suiCLI onPATH; and - network access to the chosen Sui RPC and Onara endpoint.
From a checkout:
bun install --frozen-lockfile
bun run build
bun linkThe package name is @unconfirmed/publish and the installed executable is
publish.
Networks
--network and --onara-url are always required. There are no ambient
defaults for either one; callers bring their own Onara deployment.
| Network | Default Sui gRPC |
| --- | --- |
| testnet | https://fullnode.testnet.sui.io:443 |
| mainnet | https://fullnode.mainnet.sui.io:443 |
The CLI verifies both endpoints' immutable chain identifiers before generating
the signer. Override private or mirrored Sui infrastructure explicitly with
--rpc-url; the network identity checks still apply.
The resolved Onara and RPC URLs are included in every JSON publish receipt.
Mainnet execution requires --yes and never opens a prompt. A mainnet dry run
does not require --yes.
The client uses Onara's versioned HTTP surface directly (/status, /sponsor,
and /sponsor/:digest/status), so it runs consistently under both Node and Bun.
Dry runs
publish ./move/my-package --network testnet --onara-url <URL> --dry-run --jsonA dry run compiles the package, constructs the full transaction, lets the Sui
SDK simulate it while resolving the gas budget, signs it with a new ephemeral
key, and asks Onara to validate its sponsorship policy. Onara does not submit or
sponsor-sign it. The receipt reports effect: "not_applied".
Each invocation deliberately gets a new sender. A later executable invocation therefore has different transaction bytes, while retaining the same modules, dependencies, package digest, and command shape.
Machine contract
Use --json for one JSON result document on stdout. Progress is JSONL on stderr
and can be disabled with --quiet. Before a result is available, stdout stays
empty on failure.
publish . --network testnet --onara-url <URL> --dry-run --json --quiet{
"schemaVersion": 1,
"operation": "publish",
"outcome": "validated",
"effect": "not_applied",
"network": "testnet",
"packagePath": "/absolute/path/to/package",
"packageDigest": "0x...",
"moduleCount": 1,
"dependencyCount": 2,
"sender": "0x...",
"sponsor": "0x...",
"immutable": true,
"onaraUrl": "<URL>",
"rpcUrl": "https://fullnode.testnet.sui.io:443",
"suiCliVersion": "sui 1.78.1-...",
"policy": "allow-all"
}An executed receipt additionally includes transactionDigest, packageId, and
the Published.toml update state. Field names and enum values are part of the
CLI's public protocol; additive fields may be introduced in schema version 1.
Errors are structured on stderr:
{
"type": "error",
"error": {
"code": "SPONSORSHIP_DENIED",
"message": "Transaction is not eligible for sponsorship.",
"effect": "not_applied"
}
}Exit statuses are typed:
| Status | Meaning |
| --- | --- |
| 0 | Publish, dry run, or status read succeeded. |
| 1 | Operational failure with a known applied/not-applied outcome. |
| 2 | Invalid usage or missing mainnet confirmation. |
| 3 | Submission outcome is unknown; reconcile before retrying. |
The CLI does not retry the publish request. A timeout or lost response can hide a committed transaction, and retrying with a fresh key would create another package. The locally derived transaction digest is returned whenever possible.
publish status <TRANSACTION_DIGEST> --network testnet --onara-url <URL> --jsonImportant package constraints
Immutability is permanent. A successful package cannot be upgraded, rolled back, or repaired through an upgrade.
The transaction sender is a disposable address. If a package init function
creates an admin capability or any other address-owned object for
tx_context::sender, that object will be owned by the ephemeral address and
become inaccessible after the process exits. Use this tool only for packages
whose initialization leaves no authority or assets that must remain controlled.
Published.toml is local deployment metadata, not proof that the transaction
succeeded. The onchain transaction digest and package ID in the success receipt
are authoritative.
Full help
publish --helpHelp, version, and argument errors are local and do not initialize the SDK, inspect credentials, or contact a network.
License
MIT
