Quickstart.

From an empty directory to an application running full screen on a device. Fifteen minutes if the hardware is already provisioned, plus however long your build takes.

Preview — requires early access

Before you start

You need three things: an ArdaForm account (early access, for now), at least one registered device, and any toolchain that produces static HTML, CSS and JavaScript. Vite, esbuild, a Makefile or a folder of hand-written files all work identically — ArdaPlayer loads the output, not the tooling.

Worth installing first ArdaLive is our free VS Code extension for the authoring loop: live HTML preview that patches the DOM in place instead of reloading, so a multi-step kiosk screen keeps its state while you edit it. It also serves your unsaved buffer, so you see the change before you save. Independent of ArdaForm — it works on any plain HTML/CSS project.
i
No device yet? The provisioning path covers turning bare hardware into a registered device. A workstation simulator is planned but not available yet, so this guide assumes real hardware.

Install the CLI

The CLI is a single static binary with no runtime dependency.

shell workstation
$ curl -fsSL https://ardaform.net/install.sh | sh
$ arda version
arda 0.4.0-preview  linux/amd64

$ arda login
Opening browser for authentication…
 authenticated as [email protected]

On a CI runner, skip arda login and set ARDA_TOKEN in the environment instead.

Create a project

shell workstation
$ arda init counter-kiosk
  created  counter-kiosk/arda.json
  created  counter-kiosk/src/index.html
  created  counter-kiosk/src/app.js
  created  counter-kiosk/src/style.css

$ cd counter-kiosk
$ arda link
  fleet    counter-kiosk
  channel  beta
 workspace linked

The manifest

arda.json is the only file ArdaForm requires. It names the application, points at the entry HTML, and — most importantly — declares which device capabilities the package may use.

arda.json
{
  "name":    "counter-kiosk",
  "version": "0.1.0",
  "entry":   "src/index.html",

  // Run before packaging. Omit if you have no build step.
  "build":   "npm run build",
  "root":    "dist",

  // Anything not listed here is absent at runtime.
  "capabilities": [
    "db",
    "sync",
    "printer",
    "usb:read"
  ],

  "kiosk": {
    "cursor":     false,
    "blankAfter": 0
  },

  // device | installation | account | none
  "bind": "installation"
}
!
Declare narrowly Capabilities are not free. Each one you list widens what a bug or a compromised dependency in your bundle can reach. Ask for usb:read rather than usb if you only enumerate devices, and drop anything you stop using.

Write the application

This is ordinary front-end code. The only ArdaForm-specific part is the arda global, which the runtime injects before your entry script runs.

src/app.js
// The runtime resolves this once the bridge is ready
const info = await arda.system.info()
document.querySelector('#device').textContent = info.deviceId

// A local store — survives reboots, encrypted at rest
const db = await arda.db('counter')
let count = (await db.get('total')) ?? 0

render()

document.querySelector('#add').addEventListener('click', async () => {
  count++
  await db.put('total', count)

  // Durable — queued locally if the network is down
  await arda.sync.push({ type: 'increment', count })

  render()
})

// Degrade cleanly when a capability wasn't declared
if (arda.can('printer')) {
  document.querySelector('#print').hidden = false
}

function render() {
  document.querySelector('#count').textContent = count
}

Build a package

arda build runs your build command, bundles the output tree, signs it with your pack key and encrypts it. The result is a single file you could email if you wanted to — it's useless without a device authorised to open it.

shell workstation
$ arda build
  build      npm run build
  bundling   dist/ → 42 files, 1.84 MB
  manifest   caps: db, sync, printer, usb:read
  signing    ed25519 ✓
  encrypting aes-256-gcm ✓
 app.arda  sha256 4f9c…a10e

Your pack key signs locally. It is never uploaded and the control plane never holds it — see the security model.

Deploy to a device

arda push does the same build and uploads the result as an immutable release. arda deploy then points a target at it.

shell workstation
$ arda push -m "first build"
 release r-001 pushed to channel beta

$ arda devices
  DEVICE       GROUP    RELEASE  STATE    SEEN
  af-0c41d9    bench    —        online   2s

$ arda deploy r-001 --device af-0c41d9
  af-0c41d9  downloading  1.84 MB
  af-0c41d9  verifying    sha256 ✓  signature ✓
  af-0c41d9  staged       restarting…
 1 applied · 0 failed

Watch it run

Once it's live, the same terminal gets you inside the running page. This is the part that replaces driving to the site.

shell workstation
$ arda logs af-0c41d9 --follow
  12:04:11 info  app: ready in 612ms
  12:04:19 info  sync: queue drained (1 item)

$ arda console af-0c41d9
  attached — evaluates in the live page
> await arda.system.info()
  { deviceId: 'af-0c41d9…', arch: 'amd64', uptimeSec: 8511 }

$ arda screenshot af-0c41d9 -o shot.png
 1920x1080 png written to shot.png

Where next