This repository is a concrete starter for a "rank‑4+" multimodal personal agent hub:
- Authoritative Hub (single source of truth): identity graph, memory tiers, consent/policy, presence, sessions
- Satellites (devices / awareness bubbles): authenticate as unique devices, send events, receive commands
- Autonomy: scheduled ticks + background planner
- Tool execution: permission-scoped, auditable
- Tamper-evident audit: append-only objects in S3 Object Lock (WORM)
AI components (not AWS-specific):
- OpenAI Realtime API: low-latency speech-to-speech + multimodal I/O
- OpenAI Responses API: deliberative planner + tool calling
- Optional: OpenAI Agents SDK tracing
Realtime media plane:
- Designed to pair with LiveKit (Cloud first or self-host later). The Hub provides the sideband control channel and persistent state.
template.yaml— SAM/CloudFormation stack (Hub REST API, WebSocket API, Aurora Postgres Serverless v2 + Data API, SQS, S3, IAM)layers/shared— shared python library (RDS Data API, auth, policy, audit)functions/hub_api— FastAPI on Lambda (REST)functions/ws_*— WebSocket handlers (connect/disconnect/message)functions/planner— consumes transcript events, calls OpenAI Responses, writes memories + proposed actionsfunctions/tool_runner— executes approved actions (stub)apps/agent_worker— container skeleton for LiveKit Agent + OpenAI Realtimesql/— DB schemascripts/— helper scripts (DB init, backup/export)
This repo uses a Conda env named marvain (Python 3.11, matching the Lambda runtime in template.yaml).
conda env create -f config/marvain_conda.yaml
. ./marvain_activate
marvain doctorEscape hatch (not recommended): set MARVAIN_ALLOW_VENV=1 to bypass Conda checks.
If you want a true “start over”, follow QUICKSTART.md (full workflow). The commands below are the same idea, but inlined here for convenience.
Safety: the commands below can delete AWS resources and local state.
- (Recommended) capture the bucket names before teardown:
. ./marvain_activate
./bin/marvain --profile <aws-profile> --region <aws-region> monitor outputs
# optional: write outputs into your config for later reference
./bin/marvain --profile <aws-profile> --region <aws-region> monitor outputs --write-config- Tear down the stack:
./bin/marvain --profile <aws-profile> --region <aws-region> teardown --yes --waitIn template.yaml, both buckets are configured with DeletionPolicy: Retain:
ArtifactBucket(Retain)AuditBucket(Retain + S3 Object Lock, default 10-year governance retention)
That means stack deletion will not delete them.
If you captured ArtifactBucketName / AuditBucketName, try:
# Replace these with values printed by: ./bin/marvain ... monitor outputs
ARTIFACT_BUCKET="..."
AUDIT_BUCKET="..."
# Artifact bucket: usually deletable once emptied
aws s3 rb "s3://${ARTIFACT_BUCKET}" --force
# Audit bucket: may fail if it contains Object-Lock-protected objects
aws s3 rb "s3://${AUDIT_BUCKET}" --forceIf the stack still exists, you can often recover the bucket names from CloudFormation outputs:
STACK_NAME="marvain-<user>-<env>" # e.g. marvain-major-dev (from your config)
aws cloudformation describe-stacks \
--stack-name "${STACK_NAME}" \
--query 'Stacks[0].Outputs[?OutputKey==`ArtifactBucketName` || OutputKey==`AuditBucketName`].[OutputKey,OutputValue]' \
--output tableIf the stack is already deleted, you have to fall back to heuristics (less reliable). A common one is that CloudFormation-generated bucket names include the stack name, so you can list buckets and filter by substring:
STACK_NAME="marvain-<user>-<env>" # must match the stack name you deployed
aws s3api list-buckets --query 'Buckets[].Name' --output text \
| tr '\t' '\n' \
| grep "${STACK_NAME}" || trueIf you get multiple candidates, you can identify the audit bucket by checking whether Object Lock is enabled (it will typically succeed for the audit bucket and fail for the artifact bucket):
for b in $(aws s3api list-buckets --query 'Buckets[].Name' --output text | tr '\t' '\n' | grep "${STACK_NAME}" || true); do
if aws s3api get-bucket-object-lock-configuration --bucket "$b" >/dev/null 2>&1; then
echo "${b} (ObjectLock: enabled)"
else
echo "${b} (ObjectLock: not enabled / unknown)"
fi
doneIf AuditBucket deletion fails due to Object Lock retention, the practical “reset” approach is:
- leave the audit bucket alone, and/or
- deploy a new stack name (fresh resources) instead of trying to hard-delete locked audit history.
- Remove local config (this deletes your saved device token):
# OPTIONAL: backup first
cp -v "${XDG_CONFIG_HOME:-$HOME/.config}/marvain/marvain-config.yaml" \
"${XDG_CONFIG_HOME:-$HOME/.config}/marvain/marvain-config.yaml.bak" 2>/dev/null || true
# Delete config (canonical + legacy fallbacks)
rm -f "${XDG_CONFIG_HOME:-$HOME/.config}/marvain/marvain-config.yaml" \
"${XDG_CONFIG_HOME:-$HOME/.config}/marvain/marvain.yaml" \
"${XDG_CONFIG_HOME:-$HOME/.config}/marvain/config.yaml" \
"$HOME/.marvain/config.yaml"- Remove repo build artifacts:
rm -rf .aws-sam- Remove the Conda environment:
conda env remove -n marvainPrereqs:
- Conda (Miniconda/Mambaforge)
- AWS credentials configured (profile/region)
- SAM CLI
- Docker
./bin/marvain build
./bin/marvain build --dry-run./bin/marvain config init --profile <aws-profile> --region <aws-region> --env dev
./bin/marvain deploy
./bin/marvain deploy --dry-runNotes:
./bin/marvain deploydefaults to guided (interactive) SAM deploy.- For a fully non-interactive deploy (no stdin prompts), use:
./bin/marvain deploy --no-guided
deployruns asam build --cleanfirst so Lambda functions include vendored dependencies.
Notes:
marvain config initwrites to${XDG_CONFIG_HOME:-~/.config}/marvain/marvain-config.yamlby default.- Treat that config as secret once you run
marvain bootstrap(it will store a device token). - If you choose to write a repo-local config (e.g.
--write marvain.yaml), it is gitignored.
./bin/marvain init db./bin/marvain bootstrap --agent-name Forge --space-name home
./bin/marvain bootstrap --dry-run --agent-name Forge --space-name homeThe GUI runs locally on your machine and connects to deployed AWS resources (Aurora, Cognito, S3).
# Write stack outputs to marvain-config.yaml
./bin/marvain monitor outputs --write-config
# Start the local GUI server (background mode, default)
./bin/marvain gui start
# Or just `marvain gui` (defaults to start)
./bin/marvain guiVisit http://localhost:8084/ — you'll be redirected to Cognito for login.
| Command | Description |
|---|---|
marvain gui start |
Start GUI server (background by default) |
marvain gui stop |
Stop the running GUI server |
marvain gui restart |
Stop then start the GUI server |
marvain gui status |
Show whether GUI is running, PID, port |
marvain gui logs |
Show/tail GUI server logs |
Options:
# Start in foreground (blocking, Ctrl+C to stop)
./bin/marvain gui start --foreground
# Use different host/port
./bin/marvain gui start --host 0.0.0.0 --port 8080
# Disable auto-reload
./bin/marvain gui start --no-reload
# Force kill (SIGKILL instead of SIGTERM)
./bin/marvain gui stop --force
# Follow logs in real-time
./bin/marvain gui logs --follow
# Show last 100 lines of logs
./bin/marvain gui logs --lines 100Files:
- PID file:
.marvain-gui.pid(in repo root) - Log file:
.marvain-gui.log(in repo root)
The agent worker is a fully functional LiveKit voice agent that connects to OpenAI's Realtime API, ingests transcripts to the Hub, and hydrates conversation context (recent events + recalled memories) on session start.
cd apps/agent_worker
export LIVEKIT_URL=...
export LIVEKIT_API_KEY=...
export LIVEKIT_API_SECRET=...
export OPENAI_API_KEY=...
export HUB_API_BASE=...
python -m worker- Privacy mode is enforced at the Hub: when ON, events are accepted but not persisted or queued.
- Device auth uses opaque device tokens (hash stored server-side). Swap to Cognito/IoT later if desired.
- Audit is written to an S3 Object Lock bucket with a hash chain (verify offline).
- I'd like marvain to be able to manage other agents on my and its behalf.