Publishing
The elven publish command takes a companion project directory and ships it: builds, uploads, syncs the listing. One command from edit to live.
What it does
From a project directory containing .elven.json:
- Build. Runs the project's build (default:
node build.cjsif present, elsenpm run build). Skip with--no-build. - Create the listing on first publish. If
.elven.jsonhas nolistingId, the CLI creates a draft listing and writes the id back to.elven.json. - Upload the bundle. Every file in
build.outputDiris uploaded to the Elven CDN underc/{your-slug}/apps/{listingId}/.... Skip with--no-deploy. - Flip the listing live. Updates the listing to
status=activewith a freshmanifestUrlpointing at the uploaded bundle.
After this, your companion is installable from https://app.elvenvtt.com/c/{your-slug}.
.elven.json schema
{
"type": "app", // "app" | "pack" | "template"
"name": "My Companion", // Display name on the storefront
"description": "Short tagline shown on listing cards.",
"listingId": "auto-filled-on-first-publish",
"build": {
"outputDir": "dist", // Required — what to upload
"command": "node build.cjs", // Optional — default chosen automatically
"manifestFile": "marketplace.json",// Optional — file that becomes the manifestUrl
"thumbnailFile": "assets/cover.webp" // Optional — auto-set on the listing
}
}
Minimal example:
{
"type": "app",
"name": "My Companion",
"build": { "outputDir": "dist" }
}
Don't commit credentials to .elven.json. The CLI never reads or writes tokens through it.
Usage
cd my-companion/
elven publish
# Skip the build step (use existing dist/ contents):
elven publish --no-build
# Only sync metadata, skip file upload:
elven publish --no-deploy
# Run from a different directory:
elven publish -C ./packages/my-companion
What "type" means
| Type | What it is |
|---|---|
app | Interactive companion (a React/HTML/JS bundle that loads in Elven's panel iframes). Most common. |
pack | Content bundle — recipes, tokens, maps, asset libraries. |
template | Actor template — a prefab actor with components configured. |
Pricing
Free listings (default) work immediately. Paid listings (--price <cents> on listing create, or set later) require Stripe Connect onboarding — see elven seller onboard.
GitHub Actions
Auto-publish on every push to main.
- Generate a PAT at app.elvenvtt.com/developer → CLI Tokens → New Token. Save the value.
- In your companion's repo: Settings → Secrets and variables → Actions → New repository secret. Name
ELVEN_TOKEN, paste the value. - Add
.github/workflows/publish.yml:
name: Publish companion
on:
push:
branches: [main]
workflow_dispatch: {}
jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
- run: npm ci
- name: Publish to Elven
env:
ELVEN_TOKEN: ${{ secrets.ELVEN_TOKEN }}
run: npx @elvenvtt/cli publish
Push to main. The Action installs the CLI, builds your companion, and ships it. The first run creates the listing; subsequent runs update in place.
CI hardening
- The CLI uses
ELVEN_TOKENfrom env when no profile is configured. Noelven loginneeded. - Exit codes are stable — branch on them in shell wrappers. See Exit Codes.
- Pass
--json(or pipe to anything) for structured output. The CLI auto-switches to JSON when stdout isn't a TTY.