Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 62 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -189,14 +189,14 @@ permaweb-deploy deploy --arns-name my-app --sig-type ethereum --private-key "0x.

### Bundler service

Uploads go through a [Turbo](https://docs.ardrive.io/docs/turbo/) **bundler service** (HTTP API that bundles data for Arweave). By default, permaweb-deploy uses ArDrive’s production bundler (`https://upload.ardrive.io`). **`--uploader`** sets the **base URL** of the bundler service to use (scheme + host; typically no path).
Uploads go through a bundler service that accepts signed data items and posts them to Arweave. By default, permaweb-deploy uses the [Turbo](https://docs.ardrive.io/docs/turbo/) API and ArDrive’s production bundler (`https://upload.ardrive.io`). **`--uploader`** sets the **base URL** of the bundler service to use (scheme + host; typically no path).

| When to use | Example value |
| ------------------------- | ------------------------------------------------------------- |
| **Default** (omit flag) | ArDrive production bundler — same as Turbo CLI defaults |
| **Arweave bundler** | `https://up.arweave.net` |
| **Development / staging** | `https://upload.ardrive.dev` |
| **Custom or self-hosted** | Your own base URL if it implements the Turbo bundler protocol |
| When to use | Example value |
| ------------------------- | ----------------------------------------------------------------- |
| **Default** (omit flag) | ArDrive production bundler — same as Turbo CLI defaults |
| **Arweave bundler** | `https://up.arweave.net` |
| **Development / staging** | `https://upload.ardrive.dev` |
| **Custom or self-hosted** | Your own base URL if it implements the selected uploader protocol |

**Examples:**

Expand All @@ -207,10 +207,40 @@ permaweb-deploy deploy --arns-name my-app --wallet ./wallet.json --uploader http
permaweb-deploy upload --wallet ./wallet.json --deploy-folder ./dist --uploader https://up.arweave.net
```

To upload through a HyperBEAM bundler, set `--uploader-type hyperbeam` and pass the node URL:

```bash
permaweb-deploy upload \
--wallet ./wallet.json \
--deploy-folder ./dist \
--uploader-type hyperbeam \
--uploader https://hyperbeam.example.com

permaweb-deploy deploy \
--arns-name my-app \
--wallet ./wallet.json \
--deploy-folder ./dist \
--uploader-type hyperbeam \
--uploader https://hyperbeam.example.com
```

If the node follows the standard AO-paid HyperBEAM bundler flow, the CLI can fund the uploader wallet before uploading:

```bash
permaweb-deploy upload \
--wallet ./wallet.json \
--deploy-folder ./dist \
--uploader-type hyperbeam \
--uploader https://hyperbeam.example.com \
--hyperbeam-auto-fund
```

**Notes:**

- Billing and signer behavior still follow Turbo; if an alternate bundler has different rules, check that provider’s docs.
- Use a **base URL only** (e.g. `https://up.arweave.net`), not a path to a specific file or route.
- Turbo billing and signer behavior follow Turbo.
- HyperBEAM uploads require an Arweave JWK signer. With `--hyperbeam-auto-fund`, the CLI signs each data item, asks the node's `metering@1.0` device for a byte quote, sends AO to the node address from `/~meta@1.0/info/address`, imports that deposit through `/~ao-payment@1.0/ingest`, and waits for the uploader's balance at `/ledger~node-process@1.0/now/balance/<address>` before uploading. The default route is `/~bundler@1.0/item?codec-device=ans104@1.0`; override it with `--hyperbeam-upload-path` if your node exposes a different bundler route.
- `--hyperbeam-fund-amount` is an optional override for the minimum local ledger balance to ensure, in AO base units. Without it, `--hyperbeam-auto-fund` uses the node's `metering@1.0` quote for the signed byte count. Use `--hyperbeam-token-id` only for a non-default AO token process, and `--hyperbeam-ledger-id` only for a non-default local ledger profile.
- Use a **base URL only** (e.g. `https://up.arweave.net` or `https://hyperbeam.example.com`), not a path to a specific file or route.

### Command Options

Expand All @@ -229,9 +259,16 @@ permaweb-deploy upload --wallet ./wallet.json --deploy-folder ./dist --uploader
- `--max-token-amount`: Maximum token amount for on-demand payment (used with `--on-demand`)
- `--no-dedupe`: Disable deduplication (do not cache or reuse previous uploads)
- `--dedupe-cache-max-entries`: Maximum number of entries to keep in the dedupe cache (LRU). Default: `10000`
- `--uploader`: Base URL of the Turbo **bundler service** to use (default: `https://upload.ardrive.io`). See [Bundler service](#bundler-service) above.
- `--uploader`: Base URL of the bundler service to use. See [Bundler service](#bundler-service) above.
- `--uploader-type`: Upload protocol to use (`turbo` or `hyperbeam`). Default: `turbo`
- `--hyperbeam-upload-path`: HyperBEAM bundler route. Default: `/~bundler@1.0/item?codec-device=ans104@1.0`
- `--hyperbeam-auto-fund`: Automatically fund the HyperBEAM local ledger before upload
- `--hyperbeam-fund-amount`: Optional minimum HyperBEAM local ledger balance override, in token base units
- `--hyperbeam-token-id`: Advanced AO token process ID override
- `--hyperbeam-ledger-id`: Advanced local HyperBEAM ledger ID override
- `--hyperbeam-ao-state-url`: AO state endpoint used while waiting for auto-fund transfer assignment. Default: `https://state.forward.computer`

**`upload`** (no ArNS): accepts `--deploy-folder`, `--deploy-file`, wallet/signer flags, `--uploader`, `--on-demand` / `--max-token-amount`, and dedupe flags only.
**`upload`** (no ArNS): accepts `--deploy-folder`, `--deploy-file`, wallet/signer flags, uploader flags, `--on-demand` / `--max-token-amount`, and dedupe flags only.

### Deduplication

Expand Down Expand Up @@ -405,6 +442,20 @@ jobs:
max-token-amount: '2.0'
```

### With HyperBEAM

```yaml
- name: Deploy through a HyperBEAM bundler
uses: permaweb/permaweb-deploy@v1
with:
deploy-key: ${{ secrets.DEPLOY_KEY }}
arns-name: myapp
deploy-folder: ./dist
uploader-type: hyperbeam
uploader: https://hyperbeam.example.com
hyperbeam-auto-fund: 'true'
```

### Disabling Deduplication

By default, the action caches transaction IDs to avoid re-uploading unchanged files. To disable this:
Expand Down
52 changes: 52 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,34 @@ inputs:
max-token-amount:
description: 'Maximum token amount for on-demand payment (used with on-demand)'
required: false
uploader:
description: 'Bundler service base URL. For HyperBEAM, pass the node URL, for example https://hyperbeam.example.com.'
required: false
uploader-type:
description: 'Uploader protocol to use: turbo or hyperbeam'
required: false
default: 'turbo'
hyperbeam-upload-path:
description: 'HyperBEAM bundler route used when uploader-type is hyperbeam'
required: false
default: '/~bundler@1.0/item?codec-device=ans104@1.0'
hyperbeam-auto-fund:
description: 'Automatically fund the HyperBEAM local ledger before upload'
required: false
default: 'false'
hyperbeam-fund-amount:
description: 'Optional minimum HyperBEAM local ledger balance override, in token base units'
required: false
hyperbeam-token-id:
description: 'Hyperbalance token ID to fund when the node advertises multiple tokens'
required: false
hyperbeam-ledger-id:
description: 'Hyperbalance ledger ID to fund when the node advertises multiple ledgers'
required: false
hyperbeam-ao-state-url:
description: 'AO state endpoint used while waiting for HyperBEAM auto-fund transfer assignment'
required: false
default: 'https://state.forward.computer'
preview:
description: 'Enable preview mode: auto-generates undername from PR number and posts a comment with the preview URL'
required: false
Expand Down Expand Up @@ -174,6 +202,30 @@ runs:
fi
fi

# Add uploader options
ARGS="$ARGS --uploader-type ${{ inputs.uploader-type }}"
if [[ -n "${{ inputs.uploader }}" ]]; then
ARGS="$ARGS --uploader ${{ inputs.uploader }}"
fi
if [[ "${{ inputs.uploader-type }}" == "hyperbeam" ]]; then
ARGS="$ARGS --hyperbeam-upload-path ${{ inputs.hyperbeam-upload-path }}"
if [[ "${{ inputs.hyperbeam-auto-fund }}" == "true" ]]; then
ARGS="$ARGS --hyperbeam-auto-fund"
fi
if [[ -n "${{ inputs.hyperbeam-fund-amount }}" ]]; then
ARGS="$ARGS --hyperbeam-fund-amount ${{ inputs.hyperbeam-fund-amount }}"
fi
if [[ -n "${{ inputs.hyperbeam-token-id }}" ]]; then
ARGS="$ARGS --hyperbeam-token-id ${{ inputs.hyperbeam-token-id }}"
fi
if [[ -n "${{ inputs.hyperbeam-ledger-id }}" ]]; then
ARGS="$ARGS --hyperbeam-ledger-id ${{ inputs.hyperbeam-ledger-id }}"
fi
if [[ -n "${{ inputs.hyperbeam-ao-state-url }}" ]]; then
ARGS="$ARGS --hyperbeam-ao-state-url ${{ inputs.hyperbeam-ao-state-url }}"
fi
fi

# Add dedupe options
if [[ "${{ inputs.no-dedupe }}" == "true" ]]; then
ARGS="$ARGS --no-dedupe"
Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,14 @@
"dependencies": {
"@ar.io/sdk": "^3.22.1",
"@ardrive/turbo-sdk": "^1.39.1",
"@dha-team/arbundles": "^1.0.4",
"@inquirer/prompts": "^7.2.0",
"@oclif/core": "^4.0.30",
"@permaweb/aoconnect": "^0.0.85",
"boxen": "^8.0.1",
"chalk": "^5.3.0",
"cli-table3": "^0.6.5",
"hyperbalance": "github:xylophonez/hyperbalance#main",
"mime-types": "^3.0.1",
"ora": "^8.1.1",
"p-limit": "^7.2.0"
Expand Down
39 changes: 26 additions & 13 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions pnpm-workspace.yaml
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
packages:
- '.'
onlyBuiltDependencies:
- hyperbalance
26 changes: 25 additions & 1 deletion src/commands/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { promptAdvancedOptions } from '../prompts/arns.js'
import { getWalletConfig } from '../prompts/wallet.js'
import type { SignerType } from '../types/index.js'
import { extractFlags, resolveConfig } from '../utils/config-resolver.js'
import { hyperbeamBundlerLink } from '../utils/hyperbeam-uploader.js'
import { expandPath } from '../utils/path.js'
import { createSigner } from '../utils/signer.js'
import { runUploadWorkflow } from '../workflows/upload-workflow.js'
Expand All @@ -33,6 +34,7 @@ export default class Deploy extends Command {
'<%= config.bin %> deploy --arns-name my-app --sig-type ethereum --private-key "0x..."',
'<%= config.bin %> deploy --arns-name my-app --on-demand ario --max-token-amount 1000',
'<%= config.bin %> deploy --arns-name my-app --uploader https://up.arweave.net',
'<%= config.bin %> deploy --arns-name my-app --uploader-type hyperbeam --uploader https://hyperbeam.example.com',
'<%= config.bin %> upload --wallet ./wallet.json # Upload only (no ArNS update)',
]

Expand Down Expand Up @@ -90,6 +92,12 @@ export default class Deploy extends Command {
'dedupe-cache-max-entries': effectiveCacheMaxEntries,
'deploy-file': baseConfig['deploy-file'],
'deploy-folder': baseConfig['deploy-folder'],
'hyperbeam-ao-state-url': baseConfig['hyperbeam-ao-state-url'],
'hyperbeam-auto-fund': baseConfig['hyperbeam-auto-fund'],
'hyperbeam-fund-amount': baseConfig['hyperbeam-fund-amount'],
'hyperbeam-ledger-id': baseConfig['hyperbeam-ledger-id'],
'hyperbeam-token-id': baseConfig['hyperbeam-token-id'],
'hyperbeam-upload-path': baseConfig['hyperbeam-upload-path'],
'max-token-amount': advancedOptions?.maxTokenAmount || baseConfig['max-token-amount'],
'no-dedupe': baseConfig['no-dedupe'],
'on-demand': advancedOptions?.onDemand || baseConfig['on-demand'],
Expand All @@ -98,6 +106,7 @@ export default class Deploy extends Command {
'ttl-seconds': advancedOptions?.ttlSeconds || baseConfig['ttl-seconds'],
undername: advancedOptions?.undername || baseConfig.undername,
uploader: baseConfig.uploader,
'uploader-type': baseConfig['uploader-type'],
wallet: walletConfig.wallet,
}

Expand Down Expand Up @@ -201,12 +210,21 @@ export default class Deploy extends Command {
spinner.succeed('ANT record updated')

const isCI = Boolean(process.env.CI)
const bundlerLink =
deployConfig['uploader-type'] === 'hyperbeam' && deployConfig.uploader
? hyperbeamBundlerLink(deployConfig.uploader, txOrManifestId)
: undefined

if (isCI) {
this.log('Deployment Successful!')
this.log('Tx ID: ' + txOrManifestId)
if (deployConfig.uploader) {
this.log('Bundler service: ' + deployConfig.uploader)
this.log('Uploader type: ' + deployConfig['uploader-type'])
}

if (bundlerLink) {
this.log('Bundler link: ' + bundlerLink)
}

this.log('ArNS Name: ' + deployConfig['arns-name'])
Expand All @@ -226,7 +244,13 @@ export default class Deploy extends Command {
table.push(
['Tx ID', chalk.green(txOrManifestId)],
...(deployConfig.uploader
? ([['Bundler service', chalk.cyan(deployConfig.uploader)]] as [string, string][])
? ([
['Bundler service', chalk.cyan(deployConfig.uploader)],
['Uploader type', chalk.cyan(deployConfig['uploader-type'])],
] as [string, string][])
: []),
...(bundlerLink
? ([['Bundler link', chalk.yellow(bundlerLink)]] as [string, string][])
: []),
['ArNS Name', chalk.yellow(deployConfig['arns-name'])],
['Undername', chalk.yellow(deployConfig.undername)],
Expand Down
Loading
Loading