Skip to content

Instantly share code, notes, and snippets.

@alanzchen
Created April 29, 2026 01:40
Show Gist options
  • Select an option

  • Save alanzchen/c5ab63fdf8023e6628ffdf01c98dbfab to your computer and use it in GitHub Desktop.

Select an option

Save alanzchen/c5ab63fdf8023e6628ffdf01c98dbfab to your computer and use it in GitHub Desktop.
Himalaya + Ortie redacted setup templates and guide

Himalaya + Ortie Setup Guide (Redacted Template)

This guide is for setting up a new mail account on a new machine using Ortie (OAuth token flow) and Himalaya (IMAP client).

Files in this gist

  • ortie-config.redacted.toml: Ortie OAuth/account template
  • himalaya-config.redacted.toml: Himalaya account template
  • SETUP_GUIDE.md: this guide

What is intentionally redacted

Replace the following placeholders with your real values:

  • <redacted-email>
  • <redacted-name>
  • <redacted-login>
  • <redacted-app-password>
  • <redacted-token-file>
  • <redacted-ortie-account>
  • /home/<user>/...

Non-sensitive values are intentionally kept (for example OAuth client-id, auth URLs, token URLs, and scopes).

1. Install dependencies

Install ortie and himalaya so both commands are available in your shell.

Optional (if using brokered access):

uv tool install --upgrade 'clibroker[client] @ git+https://github.com/alanzchen/clibroker'

2. Install Ortie config

Copy ortie-config.redacted.toml to:

~/.config/ortie/config.toml

Fill all redacted placeholders.

Important requirement:

  • Keep offline_access in the scopes list.

Without offline_access, you may not get a refresh token, and auth can break after the access token expires.

3. Install Himalaya config

Copy himalaya-config.redacted.toml to:

~/.config/himalaya/config.toml

Fill all redacted placeholders.

4. Complete OAuth login

Run your OAuth bootstrap command (example):

~/auth.sh <account_alias>

Then complete browser sign-in.

5. Verify Ortie token health

ortie token inspect --account <ortie_account_name>

Expected result: refresh token exists (With refresh token: true or equivalent).

6. Verify Himalaya on host

himalaya folder list --account <himalaya_account_name> --output json

If this fails, fix OAuth/token state first before deeper Himalaya debugging.

7. (Optional) Brokered workflow with clibroker

If you run Himalaya through clibroker:

clibroker-client execute himalaya -- folder list --account <himalaya_account_name>
clibroker-client execute himalaya -- envelope list --account <himalaya_account_name> --folder INBOX --page 1 --page-size 20

Security recommendation:

  • Allow read operations only by default (folder list, envelope list, envelope thread, message read, controlled attachment download).
  • Deny broad mutation commands unless explicitly required.

Troubleshooting quick path

If access suddenly breaks:

  1. Re-run OAuth login.
  2. Re-check token state:
    ortie token inspect --account <ortie_account_name>
  3. Confirm refresh token is present.
  4. Re-test Himalaya:
    himalaya folder list --account <himalaya_account_name> --output json
  5. Only after that, troubleshoot broker/container/network layers.

Security notes

  • Do not commit real credentials/tokens/passwords to source control.
  • Keep token cache files permission-restricted.
  • For password-based providers (for example app passwords), use app-specific credentials and rotate periodically.
#!/usr/bin/env bash
set -euo pipefail
ACCOUNT="${1:-utd}"
LOGIN_HINT="${2:-}" # optional, e.g. <user>@<domain>
CLIENT_ID="9e5f94bc-e8a4-4e73-b8be-63364c29d753"
AUTH_URL="https://login.microsoftonline.com/common/oauth2/v2.0/authorize"
REDIRECT_URI="https://localhost/"
SCOPE="https://outlook.office.com/IMAP.AccessAsUser.All https://outlook.office.com/SMTP.Send offline_access"
TMP="$(mktemp)"
trap 'rm -f "$TMP"' EXIT
echo "[1/4] Starting Ortie auth flow..." >&2
set +e
ortie auth get --account "$ACCOUNT" --debug >"$TMP" 2>&1
status=$?
set -e
cat "$TMP" >&2
state="$(sed -n 's/^ - state: //p' "$TMP" | tail -n1)"
pkce="$(sed -n 's/^ - pkce: //p' "$TMP" | tail -n1)"
if [[ -z "$state" || -z "$pkce" ]]; then
echo "ERROR: Could not extract state/pkce from Ortie output." >&2
exit 1
fi
# Prefer the exact URL Ortie printed, if present.
auth_link="$(grep -o 'https://login.microsoftonline.com[^[:space:]]*' "$TMP" | tail -n1 || true)"
if [[ -z "$auth_link" ]]; then
echo "[2/4] Ortie did not print a link; building one locally..." >&2
code_challenge="$(
printf '%s' "$pkce" \
| openssl dgst -binary -sha256 \
| openssl base64 -A \
| tr '+/' '-_' \
| tr -d '='
)"
auth_link="$(
python3 - "$AUTH_URL" "$state" "$CLIENT_ID" "$code_challenge" "$REDIRECT_URI" "$SCOPE" "$LOGIN_HINT" <<'PY'
import sys
from urllib.parse import urlencode
auth_url, state, client_id, code_challenge, redirect_uri, scope, login_hint = sys.argv[1:]
params = {
"redirect_uri": redirect_uri,
"response_type": "code",
"scope": scope,
"client_id": client_id,
"code_challenge": code_challenge,
"code_challenge_method": "S256",
"state": state,
}
if login_hint:
params["login_hint"] = login_hint
print(f"{auth_url}?{urlencode(params)}")
PY
)"
else
echo "[2/4] Using the authorization link printed by Ortie..." >&2
fi
echo
echo "Open this URL in a browser on another machine:"
echo
echo "$auth_link"
echo
echo "After sign-in, copy the FULL redirected URL from the browser address bar."
echo "It should look like:"
echo " https://localhost/?code=...&state=..."
echo
read -r -p "Paste callback URL here: " callback_url
if [[ "$callback_url" != https://localhost/* ]]; then
echo "ERROR: Callback URL does not start with https://localhost/" >&2
exit 1
fi
echo "[3/4] Exchanging authorization code for token with Ortie..." >&2
ortie auth resume \
--account "$ACCOUNT" \
--state "$state" \
--pkce "$pkce" \
"$callback_url"
echo "[4/4] Token details:" >&2
ortie token inspect --account "$ACCOUNT"
[accounts.utdallas]
default = true
email = "<redacted-email>"
display-name = "<redacted-name>"
downloads-dir = "/home/<user>/Downloads"
backend.type = "imap"
backend.host = "outlook.office365.com"
backend.port = 993
backend.login = "<redacted-login>"
backend.encryption.type = "tls"
backend.auth.type = "oauth2"
backend.auth.method = "xoauth2"
backend.auth.client-id = "9e5f94bc-e8a4-4e73-b8be-63364c29d753"
backend.auth.auth-url = "https://login.microsoftonline.com/common/oauth2/v2.0/authorize"
backend.auth.token-url = "https://login.microsoftonline.com/common/oauth2/v2.0/token"
backend.auth.pkce = true
backend.auth.scopes = [
"https://outlook.office.com/IMAP.AccessAsUser.All",
"https://outlook.office.com/SMTP.Send",
"offline_access"
]
backend.auth.access-token.cmd = "ortie token show --account <redacted-ortie-account> --auto-refresh"
message.send.backend.type = "none"
[accounts.icloud]
email = "<redacted-email>"
backend.type = "imap"
backend.host = "imap.mail.me.com"
backend.port = 993
backend.login = "<redacted-login>"
backend.auth.type = "password"
backend.auth.raw = "<redacted-app-password>"
folder.aliases.sent = "Sent Messages"
message.send.backend.type = "none"
[accounts.utd]
default = true
client-id = "9e5f94bc-e8a4-4e73-b8be-63364c29d753"
endpoints.authorization = "https://login.microsoftonline.com/common/oauth2/v2.0/authorize"
endpoints.token = "https://login.microsoftonline.com/common/oauth2/v2.0/token"
endpoints.redirection = "https://localhost"
scopes = [
"https://outlook.office.com/IMAP.AccessAsUser.All",
"https://outlook.office.com/SMTP.Send",
"offline_access",
]
pkce = true
auto-refresh = true
storage.read.command = ["bash", "-lc", "cat ~/.cache/<redacted-token-file>.json 2>/dev/null || true"]
storage.write.command = ["bash", "-lc", "cat > ~/.cache/<redacted-token-file>.json"]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment