Ariana
Overview
Ariana is an open-source agentic development platform that runs Claude Code agents on Hetzner VPSs via the Anthropic agent SDK. Each agent session gets true cloud isolation: its own disk, Chrome profile, and execution context. Agents can provision services, run Docker containers with public HTTPS, test their own changes using computer use (browser and desktop), spawn sub-agents that fork the current disk and Chrome login state, and auto-commit and push to GitHub — all from a mobile-friendly web UI.
Ariana handles the execution layer exceptionally well. What cloud agents constantly run into is the authentication wall — email verifications, SMS OTPs, credential storage, and cross-agent isolation. Ravi provides the identity layer that makes fully autonomous cloud agents viable: a real email address, a real phone number, and an encrypted credential vault, scoped per agent.
The problem Ravi solves
When an Ariana agent is autonomously provisioning infrastructure or setting up third-party services, it hits friction that stops the task:
- Signing up for Vercel, Cloudflare, Stripe sandbox, or any SaaS requires a real email address for verification
- SMS codes arrive on a number no agent can read programmatically
- Credentials discovered during a task have nowhere safe to go
- When Ariana forks an agent (copying disk + Chrome state), forked agents risk sharing or clobbering each other’s auth tokens
- Chrome session continuity — central to Ariana’s agent-forking model — breaks when a session expires and there’s no safe credential retrieval path
Without Ravi, agents block on humans for every signup, OTP, and credential handoff. With Ravi, they proceed.
How it works together
Per-agent identity, scoped to the task lifecycle
Ariana’s architecture already thinks in terms of isolated agent sessions with their own disk and Chrome state. Ravi’s identity model maps directly: each agent gets a provisioned email address, phone number, and secrets vault. When the task ends, the identity can be decommissioned. Disposable identities, one per agent lifecycle.
# At agent spawn time — provision a fresh identity for this task
ravi identity create --name "ariana-agent-$(uuidgen)" --email "agent-$(date +%s)"
# Agent now has:
# email: agent-1741952000@in.ravi.app
# phone: +15551234567
# secrets vault: isolated, encrypted
This maps cleanly onto Ariana’s VPS lifecycle: provision identity when the VPS spins up, decommission it when the task closes.
Autonomous service signups — no human in the loop
When an Ariana agent needs to sign up for a SaaS tool as part of a task, it uses its Ravi-provisioned email, completes the form, and polls for the verification email — all without pausing for a human:
import subprocess
import json
import time
import re
def complete_email_verification():
"""Poll Ravi inbox for verification email and extract link."""
for _ in range(12): # 60-second window
result = subprocess.run(
["ravi", "email", "list", "--unread", "--json"],
capture_output=True, text=True
)
threads = json.loads(result.stdout)
for thread in threads:
if any(kw in thread["subject"].lower() for kw in ["verify", "confirm", "activate"]):
detail = subprocess.run(
["ravi", "email", "read", thread["thread_id"], "--json"],
capture_output=True, text=True
)
messages = json.loads(detail.stdout)
for msg in messages:
urls = re.findall(r'https://[^\s"<>]+confirm[^\s"<>]+', msg.get("text_content", ""))
if urls:
return urls[0]
time.sleep(5)
return None
The full signup loop — form fill, email check, link click — completes in the agent session without any human touching an inbox.
SMS OTP for cloud provider setup
Many cloud providers (AWS, GCP, cloud registrars) require a phone number for account setup and send a verification code via SMS. Ravi’s provisioned phone numbers handle this:
def get_sms_otp(wait_seconds=15):
"""Wait for and extract an OTP from the Ravi SMS inbox."""
time.sleep(wait_seconds)
result = subprocess.run(
["ravi", "sms", "list", "--unread", "--json"],
capture_output=True, text=True
)
conversations = json.loads(result.stdout)
for conv in sorted(conversations, key=lambda c: c["latest_message_dt"], reverse=True):
msgs = get_conversation_messages(conv["conversation_id"])
for msg in msgs:
match = re.search(r'\b\d{4,8}\b', msg["body"])
if match:
return match.group()
return None
Credential vault — safe handoff at runtime
When an Ariana agent takes over a task that requires existing credentials — GitHub tokens, cloud API keys, npm publish tokens — it retrieves them from Ravi’s encrypted secrets store at runtime rather than pulling from environment variables or plaintext config:
# At task start, fetch what this agent needs
export GITHUB_TOKEN=$(ravi secrets get GITHUB_TOKEN)
export CLOUDFLARE_API_TOKEN=$(ravi secrets get CLOUDFLARE_API_TOKEN)
export ANTHROPIC_API_KEY=$(ravi secrets get ANTHROPIC_API_KEY)
Secrets are E2E encrypted at rest, scoped to the agent’s identity, and never logged to disk or committed into the VPS environment.
Chrome session continuity after expiry
Ariana’s agent-forking model copies Chrome login state across sub-agents. When a web session expires mid-task (OAuth tokens time out, SaaS dashboards log out), the agent has two options: block or re-authenticate. With Ravi, it re-authenticates:
def refresh_service_session(service_domain):
"""Re-authenticate to a service using Ravi-stored credentials."""
# Retrieve stored credentials for this domain
result = subprocess.run(
["ravi", "passwords", "get", "--domain", service_domain, "--json"],
capture_output=True, text=True
)
creds = json.loads(result.stdout)
# Use creds to re-authenticate via browser automation
return creds["username"], creds["password"]
No credentials surface in plaintext. The agent unblocks itself and continues.
Setup
1. Install Ravi CLI
npm install -g @ravi-hq/cli
ravi login
2. Seed the shared credential vault
Before Ariana agents start running tasks, populate the vault with credentials they’ll need at runtime:
ravi identity use ariana-base
ravi secrets set GITHUB_TOKEN ghp_yourtoken
ravi secrets set ANTHROPIC_API_KEY sk-ant-...
ravi secrets set CLOUDFLARE_API_TOKEN your-token
# Store service passwords for re-authentication
ravi passwords create --domain github.com --username youruser --password yourpass
ravi passwords create --domain vercel.com --username youruser --password yourpass
3. Provision a per-task identity at agent spawn
In your Ariana agent setup script or automation hook, provision a fresh Ravi identity before the task starts:
#!/bin/bash
TASK_ID=$(uuidgen | cut -c1-8)
IDENTITY_NAME="ariana-${TASK_ID}"
EMAIL_LOCAL="ariana-${TASK_ID}"
ravi identity create --name "$IDENTITY_NAME" --email "$EMAIL_LOCAL"
ravi identity use "$IDENTITY_NAME"
# Copy essential secrets from base identity to this task identity
ravi secrets set GITHUB_TOKEN "$(ravi secrets get GITHUB_TOKEN --identity ariana-base)"
echo "Agent identity ready: ${IDENTITY_NAME}"
4. Clean up on task completion
# When the Ariana VPS session closes
ravi identity delete "$IDENTITY_NAME"
Disposable identities prevent stale credentials and inbox accumulation across completed tasks.
Why Ravi over shared environment variables
| Shared env vars / dotfiles | Ravi per-agent identities | |
|---|---|---|
| Email for signups | ❌ No agent-controlled inbox | ✅ Dedicated email per agent |
| SMS OTP | ❌ Requires human | ✅ Agent reads from provisioned number |
| Credential isolation | ❌ All agents share same secrets | ✅ Each vault is isolated |
| Chrome re-auth | ❌ Blocks on human | ✅ Agent retrieves and re-authenticates |
| Audit trail | ❌ No record of which agent touched what | ✅ Per-identity history |
| Secret rotation | ❌ Manual, risky across many VPSs | ✅ Update vault once |
| Forked agent contamination | ❌ Shared disk/env means shared creds | ✅ Each fork gets its own identity |
Next steps
- API Keys and Auth — how Ravi manages credentials for agents
- Aperant — parallel coding agents with per-slot identity
- Claude Code — Ravi + Claude Code integration