Auth
Sign in, sign out, rotate credentials, authenticate from CI, transfer datasets, and rename your handle.
Authoring works without an account. Anything that publishes or modifies a dataset needs you signed in.
Sign in
esker login
A browser window opens to esker.so. After you sign in, the CLI prints:
signed in as you@example.com · publishing as you
That's it. Credentials are stored at ~/.esker/credentials and used automatically by every subsequent command.
If the login times out (default 5 minutes), pass --timeout 600 to extend it.
Check who you're signed in as
esker whoami
signed in as you@example.com · publishing as you
The handle on the right is the one your pushes will publish under. Use this when:
- You're not sure if a stale terminal still has credentials.
- You renamed your handle and want to confirm the change took effect (it won't, locally — see Rename your handle).
- A push failed with
not signed inand you want to confirm.
whoami round-trips to the hub on every call. If the network is down or your token has been revoked server-side, you'll see the failure here.
Sign out
esker logout
Removes ~/.esker/credentials. Idempotent — running it twice is harmless.
When your token expires
Tokens expire on a schedule. A push or check after expiry fails with:
credentials expired — run 'esker login'
Run esker login again. Same browser flow, same one-line success.
The check is local — Esker reads the JWT's expiry claim before sending the request. So you'll see the message even when offline.
Authenticate from CI
CI runners can't open a browser. The pattern is to provision the credentials file directly.
Step 1. On a workstation that's signed in, copy the credentials file:
cat ~/.esker/credentials
Step 2. Add the contents as a CI secret (e.g. ESKER_CREDENTIALS_JSON).
Step 3. In your CI job, write the secret to a file and point the SDK at it:
# GitHub Actions
- name: Configure Esker
run: |
mkdir -p $RUNNER_TEMP/esker
echo "$ESKER_CREDENTIALS_JSON" > $RUNNER_TEMP/esker/credentials
chmod 600 $RUNNER_TEMP/esker/credentials
env:
ESKER_CREDENTIALS_JSON: ${{ secrets.ESKER_CREDENTIALS_JSON }}
- name: Push dataset
run: esker push my.domain
env:
ESKER_CREDENTIALS_PATH: ${{ runner.temp }}/esker/credentials
ESKER_CREDENTIALS_PATH overrides the default ~/.esker/credentials location. The token inside the file has the same expiry as if you ran esker login locally — rotate it before it lapses.
For machine accounts that publish on behalf of an organization, sign in once as the machine user and reuse that credentials file across all CI environments.
Rename your handle
esker config set-handle <new>
Renames the handle server-side. Old <old>/<name> URLs 301 to <new>/<name>. Lockfile-bound consumers re-resolve on their next esker sync.
Transfer dataset ownership
esker transfer <ref> <new_owner>
Hand a dataset to another handle. You'll be prompted to confirm. Old <old>/<name> URLs 301 to <new_owner>/<name> after the transfer; consumers using the lockfile re-resolve on their next esker sync.
You must own the dataset (or be an admin of the owning org) to transfer it.
Make a dataset public or private
esker visibility <ref> public
Phase 1 only supports public. Passing private exits with private not yet supported · landing in phase 2.
public does not prompt — it's safe and reversible.
What needs auth and what doesn't
| commands | authenticated? |
|---|---|
push, transfer, visibility, config set-handle |
yes |
view, pull, head, manifest, schema, search |
no — public reads |
login, logout, whoami |
self-evident |
list, run, test, check |
local only, no network |
This is why esker view archie/us.sec.companies works on a fresh machine with no credentials. Read access is a property of the dataset's visibility, not of the caller.
Where credentials live
| path | what | override |
|---|---|---|
~/.esker/credentials |
JSON: token, email, handle, expiry | ESKER_CREDENTIALS_PATH |
File mode is 0600 on Unix (best-effort — failure to chmod is silently ignored). On Windows, file ACLs aren't set; treat the file as sensitive and store it accordingly.
The format is one line of JSON:
{
"token": "<jwt>",
"user_email": "you@example.com",
"owner_handle": "you",
"expires_at": "2026-06-01T00:00:00+00:00"
}
You shouldn't normally touch the file directly — esker login and esker logout manage it — but if a tool needs to copy or rotate it (CI, dotfile sync), it's a single file you can move around.
Pointing at a different hub
By default, the SDK talks to localhost:3001 (API) and localhost:3000 (web). For production, set:
export ESKER_HUB_URL=https://hub.esker.so
export ESKER_WEB_URL=https://esker.so
Set these in your shell profile (or your CI environment). There is no .env file convention — Esker reads them straight from the process environment on every call.
Different environments (staging, production, a self-hosted instance) are just different ESKER_HUB_URL values. Credentials are scoped to the hub that issued them — you can't sign into staging and push to production with the same token.
Troubleshooting
not signed in — run 'esker login' — no credentials file exists, or it's at a path the SDK isn't looking. Confirm with ls ~/.esker/credentials (or wherever ESKER_CREDENTIALS_PATH points).
credentials expired — run 'esker login' — the JWT's expiry has passed. Re-authenticate.
hub 401: <message> — the server rejected the token. Causes: the token was revoked, the handle was deleted, the hub's signing keys rotated. Re-authenticate.
RemoteDisconnected / ConnectionRefusedError — the hub isn't reachable. Check ESKER_HUB_URL and your network.
Pushes go under the wrong handle after set-handle — the local credentials file is stale. Run esker login to refresh.
See also
- Handles — what an owner handle is
- Caching — every disk path the SDK touches
- Errors and footguns — the full error catalog