Skip to content

Custom Providers

Ghostvault supports any website — not just Google. Built-in providers (google, github, facebook) work out of the box. For any other site, create a custom provider recipe.

What is a provider?

A provider is a data-driven login recipe. It describes:

  • Where to go (login_url)
  • How to detect success (auth_cookie_names + success_url_fragments)
  • How to check if session expired (session_probe_url)
  • How to sign out (logout_url)

Each profile (account) is a container — it holds sessions for every website equally. Google is just one provider; it's not a "parent" that others depend on.

Create a custom provider

await gv.create_provider(
    name="my_bank",
    login_url="https://mybank.com/login",
    success_url_fragments=["mybank.com/dashboard"],
    auth_cookie_names=["session_id"],
    auth_cookie_domain="https://mybank.com",
    session_probe_url="https://mybank.com/account",
    login_url_fragments=["mybank.com/login"],
    description="My bank login",
)

Then use it:

await gv.create_account(name="Banking", provider="my_bank")
await gv.sign_in("acc_xyz", provider="my_bank")
# → browser opens mybank.com/login → user logs in → session saved

Login levels

Level What it does When
1 (manual) Open login_url, user does everything Always available
2 (auto-fill) Execute login_steps (fill/click) Provider has login_steps + credentials stored
3 (recording) Replay recorded actions Provider has recorded_steps

Auto-selection: gv_sign_in picks the highest available level. If Level 2/3 fails → falls back to Level 1.

Detect login options on a page

Not sure what a page offers? Scan it:

await gv.detect_login_options(url="https://app.example.com/login")
# → {sso_providers: ["google", "github"], has_form: true,
#    recommendation: "ask_user"}

The agent should ask the user which provider to use when multiple SSO options are detected.

Level 2: form auto-fill

Add login_steps to the provider:

await gv.update_provider("internal_app", login_steps=[
    {"action": "fill", "target": "#username", "credential_key": "username"},
    {"action": "fill", "target": "#password", "credential_key": "password"},
    {"action": "click", "target": "button[type=submit]"},
])

Store credentials:

await gv.set_credentials("acc_xyz", credentials={
    "username": "john",
    "password": "secret",
})

Then sign in — it auto-fills:

await gv.sign_in("acc_xyz")
# → fills username + password → clicks submit → waits for success

Level 3: record + replay

Record a login flow once, replay automatically next time:

# Record (user completes login manually in the browser):
await gv.record_login("acc_xyz", provider="my_bank")
# → browser opens → JS listener captures every click/fill
# → user logs in (including 2FA, captcha, whatever)
# → steps saved to provider config

# Replay (next time):
await gv.sign_in("acc_xyz")
# → replays recorded steps → pauses for credentials/captcha

Level 3 limitations

  • Selectors may break if the site changes its UI (re-record needed)
  • Captcha/2FA always require user interaction (replay pauses)
  • Passwords are never recorded — store them via gv_set_credentials

Provider management

await gv.list_providers()         # all providers + capability flags
await gv.get_provider("github")   # full config
await gv.update_provider("my_bank", login_url="https://new.mybank.com/login")
await gv.delete_provider("my_bank")  # built-ins can't be deleted

Session health check

await gv.check_session("acc_xyz")
# → {health: "active", reason: "Session is active"}
# → {health: "expired", reason: "Redirected to login page"}

Storage

Provider recipes are stored in ~/.ghostvault/providers.json (shared across all accounts). Built-in providers (google, github, facebook, generic) are hardcoded and cannot be modified.

Credentials are stored per-account in the fingerprint bundle (encrypted when encryption-at-rest is enabled).