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.

Staged rollouts

Set a rollout percentage when creating or editing a release (number input with 5/25/50/100 presets), and raise it with Bump rollout. Devices are bucketed by their stable device id, keep their bucket while the percentage climbs, and gated-out devices receive the previous active release. Clients without a device id (older SDKs) only receive fully rolled-out releases.

Each release row shows update-check analytics: how many devices are already on this version and how many were offered it (update checks from older clients), so you can see real rollout coverage as the percentage climbs.

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.

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.