Product
Sign in
Install Oko, request and verify an email code, select an explicit autonomous method, and diagnose the actual refusal in CLI or Desktop.
Sign in to your team's Oko#
Quickstart: inspect your team's work from a new Mac#
Use this path when you already belong to a Wisent organization and want to inspect its transcript-backed work without granting Oko access to your inbox. Requesting a code does not create an account or organization. Your team's identity administrator must have added your account first.
Install the CLI
Oko supports macOS 14 or newer. Follow the source installation procedure:
git clone https://github.com/wisent-ai/oko.git
cd oko
cargo build --manifest-path rust/Cargo.toml --release --locked
rust/target/release/oko installThe signed installation requires Stado and a stable signing identity as described on that page. Oko Desktop is installed separately and does not install or replace the CLI. Installation is not proof of a usable account or database connection.
Select the database, then request a code
An existing Wisent installation resolves the Oko database through Stado and Skarbiec. If you connect to a separately managed deployment without those integrations, supply both OKO_DATABASE_URL and OKO_DATABASE_ANON_KEY through your process environment, using the endpoint and public client key from that deployment. Setting only one, setting an empty value, or supplying a value that is not valid Unicode is refused with the variable's name before cache or Stado resolution. Unset both to select managed resolution; invalid inputs never select another endpoint after a failed connection. The public client key is not a member's session and grants no database-owner authority.
oko login --email <your-account-address> --request-codeRead the email in your normal mail application, then enter its code:
oko login --email <your-account-address> --code <received-code>
oko database check --jsonThe first command returns email and requestedAt when the provider accepts the request. It does not claim delivery or replace the current session. Verification saves the authenticated session and returns its userId and expiry. database check proves the database consumer can resolve that member's organization; inspect its user_id, email and org_id, not merely whether a session file exists. A member of several organizations must select one with OKO_ORGANIZATION_ID; Oko reports the available IDs rather than guessing.
You can now continue with source adoption or the task ledger. A code request and verification need neither Skrzynka nor database-owner credentials.
The same flow in Desktop
Open Model access → Wisent session for oko, enter your account address and choose Code from my inbox. Select Request code, read the email in your mail application, enter the code and select Sign in. The request result shows the address and request time separately from the signed-in member. A failed request or sign-in keeps its actual operation and cause visible; it does not display a successful session or select another method. A successful sign-in or sign-out invalidates the previously displayed member before reading the new identity. If that read fails, Desktop shows the cause instead of continuing to label the previous account as signed in. Results from an older member read cannot replace a newer read.
Explicit autonomous methods#
Choose only the authority you intend to use:
oko login --email <account-address> --method mailbox \
--mailbox-organization <organization-holding-the-mailbox>
oko login --email <account-address> --method database-ownerThe mailbox method uses that exact Skrzynka organization for mailbox discovery and reading. Its credentials remain under Skrzynka's Skarbiec integration; connect the real mailbox using the Skrzynka quick-start. The database-owner method requires the configured owner's existing grant. It is not a fallback for an unreadable inbox. A refusal ends the selected attempt, and changing methods requires a new explicit invocation. Desktop exposes the same choices; the mailbox organization field appears only for Read mailbox with Skrzynka.
Independent session profiles#
OKO_SESSION_PATH selects a session file. Its database.json resolution cache and session lock belong to the same directory, so an isolated test account does not replace the operator's default cache. Give each independent profile its own directory. Without an override, the session remains ~/.oko/auth-session.json and the resolution remains ~/.oko/database.json. A set but empty OKO_SESSION_PATH is refused. It never silently selects the default profile, including for logout. The first successful sign-in creates the selected profile directory before acquiring its session lock. An existing file where that directory should be, or an operating-system refusal to create it, reports both the directory and session path with the actual cause; authentication is not reported as saved.
oko logout --jsonLogout removes the selected session and reports removed; a repeated logout reports that no session was kept. A cached endpoint is not a signed-in member.
Refusals and their meaning#
- Missing, repeated or conflicting operation: select exactly one of
--request-code,--code, or--method. Authentication selectors and the address may occur only once. Value flags without values are refused before any authentication request. - Missing mailbox organization: supply the organization holding the mailbox in Skrzynka. That option is refused for code verification and database-owner authorization rather than silently ignored.
- Ambiguous mailbox: multiple matching addresses in the selected Skrzynka organization are refused with their mailbox IDs before a code is requested. Oko does not choose whichever row happens to be listed first.
- Provider rejection: the result retains the failed request and provider's cause. An expired, invalid or already-used code is not retried through another authentication method.
- Mailbox or grant failure: the selected dependency's actual failure is retained. An accessible credential declaration alone does not prove mailbox access or database-owner authorization.
- Session or cache failure: the file path and operating-system or decoding cause remain visible. A failed session save is not a successful sign-in.
- Organization failure: an authenticated account with no organization cannot read the team database. Several memberships require an explicit selection.
A refused refresh is recorded once#
Every Oko command — and every hook of every agent session — reads the same session file and refreshes it when its access token has expired. The refresh token is single-use, so once a process has spent it, the host refuses it for everyone (refresh_token_already_used), and many processes refreshing at once reach the host's request limit (HTTP 429). The process that meets the refusal writes it into the session file, under the same lock every refresh takes:
- Any refusal the host makes of the token itself is kept as
refusalwith its time and the host's own answer. Every later command refuses at once —the signed-in Wisent session at <path> was refused by Supabase at <time> and no refresh is sent again until a sign-in replaces it: <cause>; runoko login`` — and sends nothing. - A 429 that carries
Retry-Afteris kept asrefreshBlockedUntil; no process sends a refresh before that time, and each one says so. - A request the host never answered records nothing: the next process refreshes again.
oko login replaces the recorded session and clears both. The real check is tests/database/refresh_refusal.rs, run through the database-login target with --ignored a_refused_refresh: it seeds a session with a token the host never issued, proves one refresh is sent and recorded, that the second command sends none, and that oko logout removes the file.
The complete flags and result contract are in login, logout and database check.
Real sign-in qualification#
tests/database/login.rs is registered as the database-login Cargo target. It invokes the release-built dist/oko under WISENT_OUTPUT_DIR, checks its SOURCE_REVISION against WISENT_SOURCE_COMMIT, and retains the declared WISENT_SOURCE_SHA256, measured executable and journey digests, actual command outputs, exit statuses and process-start errors. These are the same release inputs produced by Oko's Stado build, not the current checkout's Git HEAD. Candidate preparation also writes setupError into the run's report when release inputs are absent or empty, SOURCE_REVISION cannot be read or does not match, or the executable cannot be read. These failures occur before any authentication request. A report-write failure names its path and operating-system cause; if preparation already failed, that original cause is preserved too.
Run the journey only against a dedicated existing account. Supply OKO_TEST_LOGIN_EMAIL, OKO_TEST_LOGIN_USER_ID and OKO_TEST_MAILBOX_ORGANIZATION; that organization's real Skrzynka mailbox must belong to the account. The refresh-refusal case also requires the host's existing database endpoint/public-key resolution, which it reads without changing. Both cases select their own session and resolution-cache directory under .build/, leaving the operator's session untouched.
cargo test --manifest-path rust/Cargo.toml --test database-login -- --ignored --nocaptureThe mailbox journey starts with an absent profile directory and checks selector refusals, a provider-rejected code, an absent mailbox, successful first sign-in, the actual database member and persisted profile resolution, then logout. The refresh journey checks a real provider refusal, its persisted cause and refusal on the next command without another refresh request. Retain each .build/login-*/report.json and .build/refresh-*/report.json; an authored or registered journey is not a passing run. These CLI cases do not establish Desktop rendering or the separate request-code → inbox → code-verification success path. A separate profile journey exercises logout before a profile exists, then an occupied directory. It requires the concrete directory, session path and operating-system cause in the refusal and checks that the existing file was not changed. Its receipt is .build/profile-*/report.json; it makes no provider request and does not replace the real sign-in journey.
The registered environment journey invokes the same release-built CLI with empty, whitespace-only and non-Unicode values for each database variable. It requires the variable and actual input cause in the refusal before any cached or managed endpoint can be selected, then checks both profile files remain unchanged. Evidence is retained under .build/login-environment-*/.
Migrated from src/content/docs/sign-in/index.md