Connect a workspace
Give a workspace a registry URL and a credential, with a device login or an access token, locally and in CI.
A connected workspace publishes to a registry and checks content out of it. It needs a registry URL and a credential. A device login and an access token give the same kind of credential.
Device login
npm run login -- --url https://registry.example.com- The CLI shows a device code and opens the registry's approval page.
- Sign in to the registry, enter the code, and approve the CLI.
- The CLI saves the credential in
.handoff/cli-auth.jsonfor that exact registry URL.
- Include the base path in
--urlif the registry has one. Without--url, the CLI uses the configured registry URL. --no-browser, orHANDOFF_LOGIN_NO_BROWSER=true, prints the approval URL without opening it.- The login carries the scopes of your role. Publishing needs an administrator.
Several registries
The CLI saves one login per profile:
npm run login -- --profile staging --url https://staging.example.com
npm run publish -- all --profile stagingA login profile name needs no handoff.config.<name>.* file, and login is the only command that
accepts a new name. A profile without its own login uses the default login.
Sign out
npm run logout
npm run logout -- --profile staging
npm run logout -- --alllogout revokes the saved token when it can, then removes the login of the selected profile.
--url limits it to a login for that exact URL. --all removes every saved login.
Access token
Create a token in the registry under Account → Access tokens. Publishing needs a Read/write token; checkout needs Read. See Accounts and access.
HANDOFF_REGISTRY_URL=https://registry.example.com
HANDOFF_REGISTRY_ACCESS_TOKEN=hnd_...These two variables are the defaults of runtime.registryConnection, so no config is needed. A
selected profile reads .env.<profile> on top of .env, so each profile can point at a different
registry. Add a config block only to keep the URL in the repository or to read different variable
names:
runtime: {
registryConnection: {
url: 'https://registry.example.com',
accessToken: fromEnv('HANDOFF_REGISTRY_ACCESS_TOKEN'),
},
},Which credential wins
publish and checkout look for a credential in this order:
HANDOFF_REGISTRY_URLandHANDOFF_REGISTRY_ACCESS_TOKEN, orruntime.registryConnection.- The saved login of the selected profile, if it belongs to the same registry URL. A profile without a login uses the default login.
HANDOFF_SYNC_SECRET, when there is no token and no login.
Environment values win over a saved login, so a CI job behaves the same on a machine where a
developer is signed in. If the environment names a different registry than the profile's login,
publish and checkout say which login they skipped and why.
Publish from CI
name: Publish to registry
on:
push:
branches: [main]
jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 24
cache: npm
- run: npm ci
- run: npm run publish -- all
env:
HANDOFF_REGISTRY_URL: ${{ vars.HANDOFF_REGISTRY_URL }}
HANDOFF_REGISTRY_ACCESS_TOKEN: ${{ secrets.HANDOFF_REGISTRY_ACCESS_TOKEN }}
HANDOFF_FIGMA_PROJECT_ID: ${{ vars.HANDOFF_FIGMA_PROJECT_ID }}
HANDOFF_DEV_ACCESS_TOKEN: ${{ secrets.HANDOFF_DEV_ACCESS_TOKEN }}- The token needs read and write access, so an administrator creates it.
publish tokensandpublish assetsrun the Figma pipeline, soallneeds the Figma variables. To publish only catalog items and pages, runpublish catalogandpublish pagesand drop the Figma variables.