{"id":50222340,"url":"https://github.com/Scottpedia0/access","last_synced_at":"2026-06-03T16:00:48.225Z","repository":{"id":349331262,"uuid":"1201925994","full_name":"Scottpedia0/access","owner":"Scottpedia0","description":"One Bearer token, all your services. Self-hosted credential store + API proxy + MCP server for agents and scripts.","archived":false,"fork":false,"pushed_at":"2026-04-05T12:23:43.000Z","size":277,"stargazers_count":0,"open_issues_count":6,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-04-05T12:24:39.676Z","etag":null,"topics":["ai-agents","api-proxy","claude-code","credential-management","developer-tools","gemini-cli","mcp-server","nextjs","oauth2","self-hosted"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Scottpedia0.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-04-05T10:59:50.000Z","updated_at":"2026-04-05T12:23:49.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/Scottpedia0/access","commit_stats":null,"previous_names":["scottpedia0/access"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/Scottpedia0/access","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Scottpedia0%2Faccess","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Scottpedia0%2Faccess/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Scottpedia0%2Faccess/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Scottpedia0%2Faccess/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Scottpedia0","download_url":"https://codeload.github.com/Scottpedia0/access/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Scottpedia0%2Faccess/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33872298,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-03T02:00:06.370Z","response_time":59,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ai-agents","api-proxy","claude-code","credential-management","developer-tools","gemini-cli","mcp-server","nextjs","oauth2","self-hosted"],"created_at":"2026-05-26T13:00:18.257Z","updated_at":"2026-06-03T16:00:48.219Z","avatar_url":"https://github.com/Scottpedia0.png","language":"TypeScript","funding_links":[],"categories":["Cloud Infrastructure","📦 Other"],"sub_categories":["🔒 Security"],"readme":"# Access\n\n\u003e \"It's kind of a nifty little utility.\"\n\u003e — me\n\u003e\n\u003eOnce in a while, an agent needs to be reminded that this exists.\n\n\u003e Everything below this line was drafted by the bots.\n\nOne Bearer token, all your services.\n\nAccess gives agents and scripts secure access to OAuth and API-key-backed services without handling credentials directly. Store your secrets, refresh tokens automatically, proxy requests, audit everything — through one stable interface over HTTP and MCP.\n\n```mermaid\nflowchart LR\n    A[Any MCP client\\nor HTTP caller] --\u003e|Bearer token| B[\"Access\\n(Next.js + Postgres)\"]\n    B --\u003e|OAuth 2.0| C[Google · GitHub · Sentry · Oura]\n    B --\u003e|API key| D[HubSpot · Linear · Jira · Stripe\\nNotion · Apollo · Cal · Porkbun]\n    B --\u003e|Token| E[Slack · Cloudflare · Vercel\\nGitLab · AWS]\n```\n\n## What it does\n\nYou put every credential in it — API keys, OAuth tokens, bot tokens, agent credentials, service secrets, whatever your agents and scripts need. Then you decide who gets what.\n\n- **Per-agent and per-fleet permissioning** — each agent or group gets its own token with scoped access. Coding agents see GitHub and Linear. Comms agents see Gmail and Slack. Ops agents see AWS. Managed from the admin UI — no config files, no CLI juggling.\n- **Stores everything encrypted** — API keys, OAuth tokens, bot tokens, agent-to-agent credentials, service secrets. AES-256-GCM at rest, HMAC-hashed access tokens.\n- **Handles OAuth** — token refresh, consent flows, multi-account Google — your agent never participates\n- **Proxies API calls** — for services with adapters, agents hit Access and get JSON back without ever seeing the underlying key\n- **Serves credentials directly** — for everything else, agents pull keys via `/bootstrap` or `/secrets/by-env/WHATEVER`\n- **Logs everything** — every secret access, every API call, every auth attempt, with actor and IP\n- **Bootstraps sessions** — one `/bootstrap` call gives an agent only what it's authorized to see — env vars, docs, and context\n\n**The happy path:** Agent sends Bearer token → Access handles auth, refresh, and proxying → Agent gets JSON or a bootstrap bundle back. That's it.\n\n### What it looks like\n\n**Dashboard** — services, keys, agents, and audit history at a glance:\n\n![Dashboard](docs/screenshots/dashboard.png)\n\n**Agent permissioning** — each agent gets its own token with scoped access grants:\n\n![Agents](docs/screenshots/agents.png)\n\n**Agent detail** — trust level, token prefix, last used, and grant count:\n\n![Agent detail](docs/screenshots/agent-detail.png)\n\n## 30-second example\n\n```bash\n# Set once per session (don't paste tokens directly in commands)\nexport TOKEN=\"your-token\"\nexport ACCESS=\"https://your-access-instance\"\n\n# Your agent searches Gmail through Access\ncurl -H \"Authorization: Bearer $TOKEN\" \"$ACCESS/api/v1/google/gmail?action=search\u0026q=from:alice\u0026account=work\"\n\n# Or bootstraps an entire session in one call\ncurl -H \"Authorization: Bearer $TOKEN\" \"$ACCESS/api/v1/bootstrap\"\n```\n\nWith MCP, your agent gets tools like `gmail_search`, `calendar_list`, `drive_list` — no configuration per service, no expired tokens, no credential management.\n\n## Who this is for\n\n**Good fit:**\n- Running AI agents (Claude Code, Cursor, Gemini CLI, Codex) across multiple sessions or machines\n- Multi-agent setups where agents need credentials for other agents, bots, and internal services\n- Self-hosted personal or small-team setups\n- Multi-service workflows where agents need Gmail, Slack, GitHub, etc.\n- Anyone tired of bootstrapping agent sessions with scattered `.env` files\n\n**Not a fit:**\n- Enterprise secrets management with compliance requirements (use HashiCorp Vault)\n- High-compliance infrastructure with KMS/HSM requirements\n- Large team IAM or multi-tenant access control\n\n**How it differs from a password manager:** 1Password stores credentials for humans to copy-paste. Access stores credentials and *uses them* — proxying API calls, refreshing OAuth tokens, bootstrapping agent sessions. Your agent never sees the raw key for proxied services.\n\n## Security posture\n\n**What Access protects against:**\n- Agents seeing or storing raw credentials\n- Expired OAuth tokens breaking agent sessions\n- Unaudited credential access across machines\n- Plaintext secrets in databases (AES-256-GCM encryption at rest)\n- Brute-force token guessing (HMAC-SHA256 hashed, constant-time comparison)\n\n**What Access does not protect against:**\n- A compromised Access instance (if someone gets your server, they get everything)\n- Cloud-grade key management (no KMS/HSM integration yet — see roadmap)\n- Multi-tenant isolation (this is a single-owner system)\n- Network-level attacks (deploy behind HTTPS, use a firewall)\n\n## Why not just use `.env` files?\n\n- **OAuth tokens expire.** Google access tokens last 60 minutes. Your agent can't refresh them — Access can.\n- **Credentials scatter.** Each agent session needs its own copy. Rotate a key and you're updating it in 6 places.\n- **No audit trail.** Which agent accessed which service? When? From where? No idea.\n- **Bootstrapping is painful.** Every new session starts with loading env vars and hoping nothing expired.\n\n## Existing solutions (and where Access fits)\n\nThere are real tools for parts of this problem. Most solve one slice:\n\n- **Secret managers** ([1Password CLI](https://developer.1password.com/docs/cli/secrets-scripts/), [Doppler](https://docs.doppler.com/docs/cli), [Infisical](https://infisical.com/docs/documentation/getting-started/cli)) — inject static secrets at runtime via `op run` / `doppler run`. Great for API keys. Don't handle OAuth refresh or API proxying.\n- **Workload identity / OIDC** ([GitHub Actions OIDC](https://docs.github.com/en/actions/reference/security/oidc)) — avoid long-lived secrets entirely by proving identity for short-lived credentials. Great for CI/CD. Doesn't help with local agent sessions.\n- **Dynamic secrets** ([Vault dynamic secrets](https://developer.hashicorp.com/vault/tutorials/getting-started/getting-started-dynamic-secrets)) — mint time-bound credentials on demand. Serious infrastructure. Overkill for most agent setups.\n- **OAuth brokers** ([Nango](https://docs.nango.dev/guides), [Composio](https://docs.composio.dev/docs/authenticating-tools)) — handle OAuth authorization, token storage, and refresh. Cloud-first platforms with their own dashboards and billing.\n\nMature orgs split the problem across Vault + OIDC + OAuth brokers + internal platform tooling. Smaller teams use 1Password/Doppler for static secrets and still suffer on OAuth.\n\nAccess collapses these layers into one self-hosted app: store credentials, refresh OAuth, proxy API calls, bootstrap agent sessions, audit everything. It's less secure than Vault + KMS, but it's one thing instead of four — and it actually ships.\n\n| | Access | `.env` files | 1Password/Doppler | Nango/Composio | Vault |\n|---|--------|-------------|-------------------|----------------|-------|\n| Self-hosted | Yes | Yes | Varies | Cloud-first | Yes |\n| OAuth refresh | Automatic | Manual | No | Yes | No |\n| API proxying | Yes | No | No | Some | No |\n| MCP server | Built-in | No | No | No | No |\n| Agent bootstrapping | One call | Manual | No | No | No |\n| Audit trail | Yes | No | Yes | Varies | Yes |\n| Complexity | One app | None | CLI + cloud | Platform | Significant |\n| Cost | Free | Free | Paid | Paid | Free / paid |\n\n## Quick Start\n\n### Prerequisites\n\n- Node.js 20+\n- PostgreSQL (or use the included Docker Compose)\n- A Google Cloud OAuth app (if you want Google API proxying)\n\n### 1. Clone and install\n\n```bash\ngit clone https://github.com/Scottpedia0/access.git\ncd access\nnpm install\n```\n\n### 2. Set up the database\n\n```bash\n# Option A: Use Docker Compose\ndocker compose up -d\n\n# Option B: Use your own Postgres\n# Set DATABASE_URL and DIRECT_DATABASE_URL in .env\n```\n\n### 3. Configure environment\n\n```bash\ncp .env.example .env\n\n# Generate required secrets\nopenssl rand -base64 32  # -\u003e SECRET_ENCRYPTION_KEY\nopenssl rand -base64 32  # -\u003e NEXTAUTH_SECRET\nopenssl rand -base64 32  # -\u003e CONSUMER_TOKEN_HASH_SECRET\n```\n\nEdit `.env` with your values. At minimum you need:\n- `DATABASE_URL` / `DIRECT_DATABASE_URL`\n- `SECRET_ENCRYPTION_KEY`\n- `NEXTAUTH_SECRET`\n- `OWNER_EMAILS` (comma-separated list of emails allowed to log in)\n- One auth provider (Google OAuth, email magic link, or owner password)\n\n### 4. Run migrations and seed\n\n```bash\nnpx prisma migrate deploy\nnpm run db:seed  # Creates example services and a consumer token\n```\n\n### 5. Start the app\n\n```bash\nnpm run dev\n```\n\nVisit `http://localhost:3000` and sign in with an email from your `OWNER_EMAILS` list.\n\n### 6. Install agent skill + MCP config\n\n```bash\nbash scripts/install.sh\n```\n\nThis detects your installed agent harnesses (Claude Code, Cursor, Gemini CLI, Windsurf, VS Code, Codex), installs the health-check skill, and shows you the MCP config for each. You can accept or reject each step.\n\n## Supported Services\n\n27 service endpoints across `/api/v1/*`. Each adapter handles auth and proxies requests upstream.\n\n**Google Workspace** (OAuth 2.0, multi-account) — Gmail, Calendar, Drive, Sheets, Docs, Contacts, Analytics, Search Console, Tag Manager, Admin Reports, Profile\n\n**Developer tools** — GitHub, GitLab, Linear, Jira, Notion, Sentry, Vercel\n\n**Business** — HubSpot, Slack, Stripe (read-only), Apollo.io, Cal.com\n\n**Infrastructure** — AWS (S3, EC2, Lambda, CloudWatch — optional SDK deps), Cloudflare\n\n**Other** — Oura Ring, Porkbun\n\nGoogle services support multiple accounts — configure via the `GOOGLE_ACCOUNTS` env var (e.g., `work:me@company.com,personal:me@gmail.com`). Adding a new adapter is ~100 lines — see [Adding a New Service](#adding-a-new-service).\n\n### Core Endpoints\n\nThese aren't service proxies — they're Access itself:\n\n| Endpoint | What it does |\n|----------|-------------|\n| `GET /api/v1/bootstrap` | One pull that returns all secrets as env vars + service metadata + docs + linked resources. This is how agents bootstrap a session. |\n| `POST /api/v1/intake` | Write-only endpoint for submitting new credentials without read access to the store. |\n| `GET /api/v1/secrets/by-env/:name` | Look up a single decrypted secret by its env var name. |\n| `GET /api/v1/services/:slug` | Service metadata, docs, and linked resources. |\n| `GET /api/v1/services/:slug/secrets` | Decrypted secrets for a specific service. |\n\n## Authentication\n\nAccess supports three token types for agent authentication:\n\n| Token Type | Scope | Use case |\n|-----------|-------|----------|\n| **Global Agent Token** | Full access to all services and secrets | Trusted single-operator setups |\n| **Consumer Tokens** | Granular per-service or per-secret access grants | Multi-agent setups where each agent or fleet gets different permissions |\n| **Shared Intake Token** | Write-only credential submission | Let team members drop keys without read access |\n\n### Permissioning by agent or fleet\n\nConsumer tokens let you segment access by role. Each consumer gets its own identity, token, and scoped grants:\n\n```\nCoding agents (Claude Code, Cursor)  →  GitHub, Linear, Sentry\nComms agents                         →  Gmail, Slack, Calendar\nOps agents                           →  AWS, Cloudflare, Vercel\nIntake-only (team members)           →  Write keys, can't read anything\n```\n\nGrants work at two levels — **whole service** (agent sees everything in that service) or **individual secrets** (agent sees only specific keys). When an agent calls `/bootstrap`, it only gets back what it's authorized to see.\n\n```bash\n# Search Gmail with a global token\ncurl -H \"Authorization: Bearer YOUR_TOKEN\" \\\n  \"http://localhost:3000/api/v1/google/gmail?action=search\u0026q=from:alice\u0026account=work\"\n\n# Bootstrap an agent session — pull only what this token is authorized for\ncurl -H \"Authorization: Bearer YOUR_TOKEN\" \\\n  \"http://localhost:3000/api/v1/bootstrap\"\n```\n\nHuman authentication for the admin UI uses Google OAuth, email magic links, or a simple password — configured via env vars. Only emails in `OWNER_EMAILS` can log in.\n\n## Adding a New Service\n\nEach proxy adapter is a Next.js route handler under `src/app/api/v1/\u003cservice\u003e/route.ts`. To add one:\n\n1. Create `src/app/api/v1/your-service/route.ts`\n2. Use `authenticateRequestActor()` from `@/lib/access` for auth\n3. Read the API key from the encrypted store (via Prisma) or env vars\n4. Proxy the request to the upstream API\n5. Return the result\n\nMost adapters are under 100 lines. See `src/app/api/v1/hubspot/route.ts` for a clean example.\n\n## Agent Instructions\n\n`AGENTS.md` in this repo has two sections:\n\n1. **For agents developing on Access** — architecture, patterns, data model, commands\n2. **For agents using Access** — a ready-to-paste block for your `CLAUDE.md` or agent instructions that tells your agents how to bootstrap, pull credentials, and use proxy endpoints\n\nCopy the \"For agents USING Access\" section into your agent's instruction file and set `ACCESS_BASE_URL` and `ACCESS_TOKEN` in your environment.\n\n## MCP Server\n\nAccess includes an MCP server (`mcp-server.mjs`) that exposes Google Workspace tools via stdio transport. Works with any MCP-compatible client.\n\nAdd the following config to your client. The JSON is the same — only the file path changes per client:\n\n| Client | Config location |\n|--------|----------------|\n| **Claude Code** | `~/.claude/mcp.json` or project `.mcp.json` |\n| **Cursor** | Cursor MCP settings |\n| **Gemini CLI** | `.gemini/settings.json` |\n| **Windsurf** | Windsurf MCP settings |\n| **VS Code (Copilot)** | `.vscode/mcp.json` (use `\"servers\"` instead of `\"mcpServers\"`) |\n| **Codex / other** | Any MCP-compatible config |\n\n```json\n{\n  \"mcpServers\": {\n    \"access\": {\n      \"command\": \"node\",\n      \"args\": [\"/path/to/access/mcp-server.mjs\"],\n      \"env\": {\n        \"ACCESS_BASE_URL\": \"http://localhost:3000\",\n        \"GLOBAL_AGENT_TOKEN\": \"your-token-here\"\n      }\n    }\n  }\n}\n```\n\n\u003e **VS Code note:** Use `\"servers\"` as the top-level key instead of `\"mcpServers\"`.\n\nOnce connected, your agent gets tools like `gmail_search`, `calendar_list`, `drive_list`, `contacts_search`, and more — all authenticated through Access.\n\n### Direct API (No MCP)\n\nYou don't need MCP. Any HTTP client works:\n\n```bash\ncurl -H \"Authorization: Bearer $TOKEN\" \\\n  \"http://localhost:3000/api/v1/google/gmail?action=search\u0026q=is:unread\"\n```\n\n## Architecture\n\n### How a request flows\n\n```\n1. Agent sends:     GET /api/v1/google/gmail?action=search\u0026q=from:alice\u0026account=work\n                    Authorization: Bearer amb_live_xxxx\n\n2. Middleware:       Rate limit check → Body size check → Pass\n\n3. Auth:            Validate Bearer token (HMAC comparison)\n                    Look up consumer permissions or verify global token\n\n4. Proxy:           Load OAuth credentials from Postgres (encrypted)\n                    Refresh access token if expired\n                    Forward request to Gmail API\n\n5. Response:        Return Gmail results as JSON to agent\n                    Log access in audit_events table\n```\n\n### Design principles\n\n- **Agents never see credentials.** They send a Bearer token, get back API results.\n- **OAuth is handled server-side.** Token refresh, consent flows, multi-account management — all inside Access.\n- **Everything is audited.** Every secret access, every API proxy call, every login attempt is logged with actor, timestamp, and IP.\n- **Secrets are encrypted at rest.** AES-256-GCM with versioned payloads (`v2.iv.authTag.ciphertext`), key rotation supported.\n- **Consumer tokens use HMAC.** Constant-time comparison, only the prefix is stored — never the raw token.\n- **Stateless proxy.** Access doesn't cache or store API responses. It's a pass-through.\n\n## Security\n\n- AES-256-GCM encryption for all stored secrets\n- HMAC-SHA256 consumer token hashing with constant-time comparison\n- Zod input validation on all API endpoints\n- Audit logging for all access events and auth failures\n- Owner email allowlist for admin UI access\n- Error messages in production never leak upstream details\n- Health endpoint requires auth to expose inventory counts\n- Rate limiting on auth and API endpoints (configurable, in-memory by default)\n- Request body size limits on all mutating endpoints\n\n### Key Rotation\n\nAccess supports zero-downtime encryption key rotation:\n\n```bash\n# 1. Generate a new key\nopenssl rand -base64 32\n\n# 2. Set the new key and keep the old one\nSECRET_ENCRYPTION_KEY=\"\u003cnew key\u003e\"\nSECRET_ENCRYPTION_KEY_PREVIOUS=\"\u003cold key\u003e\"\n\n# 3. Re-encrypt all secrets\nnpx tsx scripts/rotate-keys.ts\n\n# 4. After success, remove the old key\n# unset SECRET_ENCRYPTION_KEY_PREVIOUS\n```\n\nThe script is idempotent — secrets already on the current key are skipped. It decrypts with whichever key works (current or previous) and re-encrypts with the current key.\n\n### Security Roadmap\n\n- [ ] Per-service scoped tokens (split global token into granular permissions)\n- [x] ~~Key rotation support~~\n- [ ] Redis-backed rate limiting for serverless\n- [ ] Envelope encryption / KMS integration\n\n## Deployment\n\nAccess deploys well on **Vercel** with a **Neon** or **Supabase** Postgres database:\n\n1. Push to GitHub\n2. Import in Vercel\n3. Set all env vars from `.env.example`\n4. Set `NEXTAUTH_URL` to your production URL\n5. Add `your-domain.com/api/google/callback` as an authorized redirect URI in Google Cloud Console\n6. Run `npx prisma migrate deploy` via Vercel build command\n\nWorks anywhere Node.js runs — Vercel, Railway, Fly.io, a VPS, your laptop.\n\n## Development\n\n```bash\nnpm run dev          # Start dev server\nnpm run build        # Production build\nnpm run lint         # ESLint\nnpm run typecheck    # TypeScript check\nnpm run db:studio    # Prisma Studio (GUI for database)\nnpm run db:seed      # Seed example data\n```\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FScottpedia0%2Faccess","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FScottpedia0%2Faccess","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FScottpedia0%2Faccess/lists"}