Hands
Hands Docs

Admin User Guide

Using the Hands admin console: apps, releases, builds, access, and troubleshooting.

Hands is a release and update distribution console for apps that need controlled binary delivery. Use the admin console to create apps, upload builds, publish releases with staged rollouts, manage share links and feedback tickets, and give teammates or automation the access they need.

Sign in

Open the Hands site and use Login with Raft. After sign-in, Hands drops you into your first app (or the app creation wizard if none exist). The left rail navigates between Apps, Org, and your account menu (Settings, Logout).

If you can sign in but do not see an expected app, ask an organization admin to grant you app access or make the app visible to your Raft server.

Apps

Each app has a left sidebar with its sections; the app switcher at the top of the sidebar jumps between apps, creates new ones, or returns to the full list.

Create one Hands app per product distribution target. For example, an Android app should have its own Hands app and release channels.

Releases

The standard flow follows the draft-first policy: CI creates a draft release with a generated changelog; a human or agent reviews it, writes the final (optionally bilingual) changelog, and publishes explicitly. See the release runbook in the repository docs.

  1. CI (or hands builds publish-android --draft) creates the draft.
  2. Review it in Releases (or hands releases show).
  3. Write the final changelog — per-language notes are supported (hands releases update … --changelog-file zh=zh.md --changelog-file en=en.md); clients receive the language matching their system locale.
  4. Publish from the release row (or hands releases publish).

For Android, Hands selects updates by version_code. A device receives an update only when the published release has a higher version_code than the client reports.

A draft, active, or superseded release reserves its app, channel, product type, release type, and version-code coordinate. Cancelling disables that lifecycle and releases the coordinate for a corrected upload while preserving the old release ID, build, assets, and audit history. An old cancelled release can be restored only until a replacement owns the version; after that, operate on the replacement release instead. For Android builds that were already distributed, use a higher version code so installed clients can receive the correction.

Staged rollouts

Set a rollout percentage when creating or editing a full release (number input with 5/25/50/100 presets), and raise it with Bump rollout. Select any number of Always included device groups on that same release for acceptance or QA devices. Group members receive the target regardless of their bucket; other devices keep a stable bucket while the percentage climbs. Gated-out and anonymous clients receive the previous eligible full release until rollout reaches 100%.

Each release row shows unique per-install devices (UV): how many devices have checked while already on this version and how many unique older devices were offered it. Hover either number to see the all-time update-check event count (PV). UV collection requires a stable device id and starts at the metric upgrade; historical PV aggregates cannot be converted into exact historical UV.

The app overview also exposes version-level metrics from GET /api/apps/:id/analytics/versions: devices that reported in the selected window, total devices seen, update-check current/offered counts, feedback/crash volume, and artifact downloads for each version/channel. The data comes from throttled SDK metrics pings, update checks, feedback tickets, and build asset download counters. These metrics show active/recent devices, not a true unthrottled online heartbeat.

Builds

Builds hold uploaded artifacts and provenance. Hands distinguishes installable artifacts from support artifacts:

Artifact kindPurpose
installableAPK or other file a client can install or download as the release payload.
proguard-mappingAndroid R8/ProGuard mapping file for crash symbolication.
native-symbolsNative debug symbols.
metadata-fileCI or build metadata archived with the build.
app-iconLauncher icon extracted automatically from the uploaded APK.

Registering an installable APK triggers automatic parsing: package id, SDK levels, and the launcher icon are extracted server-side (aapt) with no extra CI parameters. Public update checks only return installable artifacts; support artifacts remain available to authenticated admins and publishers.

Channels

Channels let you separate release tracks such as main, preview, nightly, or debug. Publish test builds to a non-production channel first, then publish to the production channel after validation.

Android clients should send the channel they are configured to use. Debug builds can point at debug; release builds typically point at main.

Shares

The Shares tab lists every public share link for the app: release, creator, expiry, status, password badge, and view/download stats. From there you can create links (with an optional password), extend them by 7 days, set or remove passwords on existing links, and revoke them. Share URLs are shown once at creation — tokens are stored hashed.

Share pages show the release's real app icon, a QR code on desktop, localized release notes, and live stats. Password-protected pages ask for the password before showing the download.

hands releases share raft-android <release-id> --password <pw>   # optional password
hands releases update-share raft-android <release-id> <share-id> --ttl-seconds 1209600
hands releases revoke-share raft-android <release-id> <share-id>

Use share pages for human review and manual testing. Use the public update API for in-app update checks.

Feedback

The Feedback tab is a lightweight ticket system for reports submitted from the app (via the SDK's HandsFeedback or the public feedback endpoint). Each ticket carries the message, contact, app version, device context (including the rollout device id), and up to nine attachments.

Crash tickets (kind=crash, submitted automatically by the SDK's crash reporter) get a grouping signature (exception class + top app frame) and a Crash groups view that aggregates by signature with occurrence and device counts. When a build's ProGuard/R8 mapping.txt was uploaded as a proguard-mapping asset for that version, Hands auto-deobfuscates the stack in the container and posts the retraced trace as a ticket comment.

Version history

Settings → Public version history exposes /apps/<slug>/history: a public page listing published versions with localized changelogs and per-version downloads. It is off by default; when disabled the page returns 404.

Access

The Access page controls who can see or publish an app.

Access typeUse
Direct app memberGive one user or agent a role on this app.
Raft server visibilityMake the app visible to accounts from another Raft server.
Deploy tokenGive CI or an agent scoped API access to this app.

For a principal on another linked Raft server, ask them to run the Hands whoami Agent Login action and send the non-secret Hands account id. In Add direct app member, paste that id instead of selecting a same-org principal. The account must have logged into Hands at least once. Hands adds missing org viewer membership and the selected app-scoped role atomically.

Deploy tokens are app-scoped bearer tokens. Create them for automation instead of reusing a human browser session. Copy the token when it is created; Hands only shows the raw token once. Each token records who created it, and actions performed with it are attributed as deploy-token:<name>@<app> in audit logs and release provenance.

Roles range from viewer (read) through member (collaborate — e.g. triage feedback), publisher (ship builds/releases), to admin (configure, manage members, hold secrets). See the permissions reference for the full role model and the endpoint→role matrix.

Common Issues

No update is found

Check that the published release is on the same channel as the client, has a higher version_code, contains a compatible installable artifact, and — for partially rolled-out releases — that the device's bucket falls inside the rollout percentage.

Public artifact URLs are signed and time-limited. Refresh the share page or run the update check again to get a fresh URL.

403 forbidden

Your account or deploy token does not have the required role for the action. The response is machine-readable so an agent or the CLI can point you straight to the fix:

{
  "error": "insufficient_org_role",       // or "insufficient_app_role"
  "required_role": "member",
  "current_role": "viewer",
  "resource": "POST /api/apps",            // the action you attempted
  "org_id": "…", "app_id": null,
  "manage_url": "https://app.hands.build/orgs/{orgId}/members"
}

To resolve, an organization admin/owner opens manage_urlOrg → Members for org roles, or an app's Access tab for app-level roles — and raises the account's role to required_role. Then retry the same request.

Note: creating an app (POST /api/apps) requires an org member or higher — an app-level member role or a deploy token is not enough. Newly Agent-Login'd agents normally start as member; viewer is a read-only role that an org admin can assign manually.