eve-auth-and-secrets
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseEve Auth and Secrets
Eve身份认证与密钥管理
Use this workflow to log in to Eve and manage secrets for your app.
使用此工作流登录Eve并管理应用密钥。
When to Use
使用场景
- Setting up a new project profile
- Authentication failures
- Adding or rotating secrets
- Secret interpolation errors during deploys
- Setting up identity providers or org invites
- Adding SSO login to an Eve-deployed app
- Setting up access groups and scoped data-plane authorization
- Configuring group-aware RLS for environment databases
- 设置新的项目配置文件
- 身份验证失败时
- 添加或轮换密钥
- 部署期间出现密钥插值错误时
- 设置身份提供商或组织邀请
- 为Eve部署的应用添加SSO登录
- 设置访问组和范围化数据平面授权
- 为环境数据库配置支持组的RLS(行级安全)
Authentication
身份认证
bash
eve auth login
eve auth login --ttl 30 # custom token TTL (1-90 days)
eve auth statusbash
eve auth login
eve auth login --ttl 30 # 自定义令牌有效期(1-90天)
eve auth statusChallenge-Response Flow
挑战-响应流程
Eve uses challenge-response authentication. The default provider is :
github_ssh- Client sends SSH public key fingerprint
- Server returns a challenge (random bytes)
- Client signs the challenge with the private key
- Server verifies the signature and issues a JWT
Eve采用挑战-响应式身份认证,默认提供商为:
github_ssh- 客户端发送SSH公钥指纹
- 服务器返回挑战信息(随机字节)
- 客户端使用私钥对挑战信息签名
- 服务器验证签名并颁发JWT
Token Types
令牌类型
| Type | Issued Via | Use Case |
|---|---|---|
| User Token | | Interactive CLI sessions |
| Job Token | Worker auto-issued | Agent execution within jobs |
| Minted Token | | Bot/service accounts |
JWT payloads include (user ID), , , and . Verify tokens via the JWKS endpoint: .
suborg_idscopeexpGET /auth/jwksRole and org membership changes take effect immediately -- the server resolves permissions from live DB memberships, not stale JWT claims. When a request includes a but no , the permission guard derives the org context from the project's owning org.
project_idorg_id| 类型 | 颁发方式 | 使用场景 |
|---|---|---|
| 用户令牌 | | 交互式CLI会话 |
| 作业令牌 | 工作节点自动颁发 | 作业内的Agent执行 |
| 生成令牌 | | 机器人/服务账户 |
JWT载荷包含(用户ID)、、和。可通过JWKS端点验证令牌:。
suborg_idscopeexpGET /auth/jwks角色和组织成员身份变更会立即生效——服务器从实时数据库成员关系解析权限,而非过期的JWT声明。当请求包含但无时,权限防护会从项目所属组织推导组织上下文。
project_idorg_idPermissions
权限检查
Check what the current token can do:
bash
eve auth permissionsRegister additional identities for multi-provider access:
bash
curl -X POST "$EVE_API_URL/auth/identities" -H "Authorization: Bearer $TOKEN" \
-d '{"provider": "nostr", "external_id": "<pubkey>"}'查看当前令牌的可用权限:
bash
eve auth permissions注册额外身份以支持多提供商访问:
bash
curl -X POST "$EVE_API_URL/auth/identities" -H "Authorization: Bearer $TOKEN" \
-d '{"provider": "nostr", "external_id": "<pubkey>"}'Identity Providers
身份提供商
Eve supports pluggable identity providers. The auth guard tries Bearer JWT first, then provider-specific request auth.
| Provider | Auth Method | Use Case |
|---|---|---|
| SSH challenge-response | Default CLI login |
| NIP-98 request auth + challenge-response | Nostr-native users |
Eve支持可插拔的身份提供商。认证防护优先尝试Bearer JWT,再使用提供商特定的请求认证方式。
| 提供商 | 认证方式 | 使用场景 |
|---|---|---|
| SSH挑战-响应 | 默认CLI登录 |
| NIP-98请求认证 + 挑战-响应 | Nostr原生用户 |
Nostr Authentication
Nostr身份认证
Two paths:
- Challenge-response: Like SSH but signs with Nostr key. Use .
eve auth login --provider nostr - NIP-98 request auth: Every API request signed with a Kind 27235 event. Stateless, no stored token.
两种路径:
- 挑战-响应:与SSH类似,但使用Nostr密钥签名。使用。
eve auth login --provider nostr - NIP-98请求认证:每个API请求都使用Kind 27235事件签名。无状态,无需存储令牌。
Org Invites
组织邀请
Invite external users via the CLI or API:
bash
undefined通过CLI或API邀请外部用户:
bash
undefinedInvite with SSH key registration (registers key so the user can log in immediately)
邀请并注册SSH密钥(注册密钥后用户可立即登录)
eve admin invite --email user@example.com --ssh-key ~/.ssh/id_ed25519.pub --org org_xxx
eve admin invite --email user@example.com --ssh-key ~/.ssh/id_ed25519.pub --org org_xxx
Invite with GitHub identity
邀请GitHub身份用户
eve admin invite --email user@example.com --github ghuser --org org_xxx
eve admin invite --email user@example.com --github ghuser --org org_xxx
Invite with web-based auth (Supabase)
使用基于Web的认证(Supabase)邀请
eve admin invite --email user@example.com --web --org org_xxx
eve admin invite --email user@example.com --web --org org_xxx
API: invite targeting a Nostr pubkey
API:邀请Nostr公钥用户
curl -X POST "$EVE_API_URL/auth/invites" -H "Authorization: Bearer $TOKEN"
-H "Content-Type: application/json"
-d '{"org_id": "org_xxx", "role": "member", "provider_hint": "nostr", "identity_hint": "<pubkey>"}'
-H "Content-Type: application/json"
-d '{"org_id": "org_xxx", "role": "member", "provider_hint": "nostr", "identity_hint": "<pubkey>"}'
If no auth method is specified (`--github`, `--ssh-key`, or `--web`), the CLI warns that the user will not be able to log in. The user can self-register later via `eve auth request-access --org "Org Name" --ssh-key ~/.ssh/id_ed25519.pub --wait`.
When the identity authenticates, Eve auto-provisions their account and org membership.
For app-driven onboarding, use the org-scoped invite API instead of the legacy admin invite flow:
```bashcurl -X POST "$EVE_API_URL/auth/invites" -H "Authorization: Bearer $TOKEN"
-H "Content-Type: application/json"
-d '{"org_id": "org_xxx", "role": "member", "provider_hint": "nostr", "identity_hint": "<pubkey>"}'
-H "Content-Type: application/json"
-d '{"org_id": "org_xxx", "role": "member", "provider_hint": "nostr", "identity_hint": "<pubkey>"}'
如果未指定认证方式(`--github`、`--ssh-key`或`--web`),CLI会警告用户无法登录。用户可稍后通过`eve auth request-access --org "Org Name" --ssh-key ~/.ssh/id_ed25519.pub --wait`自行注册。
当身份验证通过后,Eve会自动为用户创建账户并添加组织成员身份。
对于应用驱动的入职流程,使用组织范围的邀请API而非旧版管理员邀请流程:
```bashCreate an org-scoped Supabase invite with a return URL for the app
创建带有返回URL的组织范围Supabase邀请
curl -X POST "$EVE_API_URL/orgs/org_xxx/invites"
-H "Authorization: Bearer $USER_TOKEN"
-H "Content-Type: application/json"
-d '{ "email": "user@example.com", "role": "member", "redirect_to": "https://app.example.com/invite/complete", "app_context": { "project_id": "proj_123" } }'
-H "Authorization: Bearer $USER_TOKEN"
-H "Content-Type: application/json"
-d '{ "email": "user@example.com", "role": "member", "redirect_to": "https://app.example.com/invite/complete", "app_context": { "project_id": "proj_123" } }'
curl -X POST "$EVE_API_URL/orgs/org_xxx/invites"
-H "Authorization: Bearer $USER_TOKEN"
-H "Content-Type: application/json"
-d '{ "email": "user@example.com", "role": "member", "redirect_to": "https://app.example.com/invite/complete", "app_context": { "project_id": "proj_123" } }'
-H "Authorization: Bearer $USER_TOKEN"
-H "Content-Type: application/json"
-d '{ "email": "user@example.com", "role": "member", "redirect_to": "https://app.example.com/invite/complete", "app_context": { "project_id": "proj_123" } }'
Search existing org members for an assignee picker
搜索现有组织成员以选择被分配人
curl "$EVE_API_URL/orgs/org_xxx/members/search?q=ali"
-H "Authorization: Bearer $USER_TOKEN"
-H "Authorization: Bearer $USER_TOKEN"
Use a user token with `orgs:invite` to create or list these invites and `orgs:members:read` for member lookup. Invite emails should land on GoTrue's `/verify` path, not the OAuth callback directly. If the invite is auto-applied during the SSO exchange, Eve returns `invite_redirect_to` so the SSO callback can land the user back in the target app even when the email provider strips nested redirect params. Current invite onboarding establishes the SSO session first, then sends the user through `/set-password` before redirecting to the app.curl "$EVE_API_URL/orgs/org_xxx/members/search?q=ali"
-H "Authorization: Bearer $USER_TOKEN"
-H "Authorization: Bearer $USER_TOKEN"
使用具备`orgs:invite`权限的用户令牌创建或列出这些邀请,使用`orgs:members:read`权限进行成员查询。邀请邮件应指向GoTrue的`/verify`路径,而非直接指向OAuth回调。如果在SSO交换期间自动应用邀请,Eve会返回`invite_redirect_to`,以便即使邮件提供商剥离嵌套重定向参数,SSO回调仍能将用户带回目标应用。当前邀请入职流程先建立SSO会话,然后引导用户进入`/set-password`页面,最后重定向到应用。App-Branded Invite Emails
应用品牌化邀请邮件
Projects opt into app-branded invites with in the manifest. The subject, body, and display name all carry the app's identity — other apps fall back to "Eve Horizon" defaults.
x-eve.brandingFrom:yaml
x-eve:
branding:
app_name: "ACME Portal"
app_logo_url: "https://app.example.com/assets/logo.svg" # https-only
primary_color: "#1f6feb"
email_from_name: "ACME Portal"
reply_to_email: "support@example.com"
support_email: "support@example.com"
support_url: "https://example.com/help"Run after editing. Invites sent with use the project branding. The sender address remains the platform default in Phase 1; only the display name varies. The same branding template is shared with magic-link login emails — only the copy ("Accept invite" vs "Sign in") differs.
eve project synceve org invite <email> --org <org_id> --project <project_id>项目可通过清单中的选择应用品牌化邀请。邮件主题、正文和显示名称都会带有应用标识——其他应用则默认使用"Eve Horizon"。
x-eve.brandingFrom:yaml
x-eve:
branding:
app_name: "ACME Portal"
app_logo_url: "https://app.example.com/assets/logo.svg" # 仅支持https
primary_color: "#1f6feb"
email_from_name: "ACME Portal"
reply_to_email: "support@example.com"
support_email: "support@example.com"
support_url: "https://example.com/help"编辑后运行。使用发送的邀请会使用项目品牌。第一阶段中,发件地址仍为平台默认值;仅显示名称会有所不同。同一品牌模板也用于魔法链接登录邮件——仅文案("接受邀请" vs "登录")不同。
eve project synceve org invite <email> --org <org_id> --project <project_id>Token Minting (Admin)
令牌生成(管理员)
Mint tokens for bot/service users without SSH login:
bash
undefined为无需SSH登录的机器人/服务用户生成令牌:
bash
undefinedMint token for a bot user (creates user + membership if needed)
为机器人用户生成令牌(如需则创建用户和成员身份)
eve auth mint --email app-bot@example.com --org org_xxx
eve auth mint --email app-bot@example.com --org org_xxx
With custom TTL (1-90 days, default: server configured)
自定义有效期(1-90天,默认:服务器配置)
eve auth mint --email app-bot@example.com --org org_xxx --ttl 90
eve auth mint --email app-bot@example.com --org org_xxx --ttl 90
Scope to project with admin role
限定到项目并赋予管理员角色
eve auth mint --email app-bot@example.com --project proj_xxx --role admin
Print the current access token (useful for scripts):
```bash
eve auth tokeneve auth mint --email app-bot@example.com --project proj_xxx --role admin
打印当前访问令牌(适用于脚本):
```bash
eve auth tokenSelf-Service Access Requests
自助式访问请求
Users without an invite can request access:
bash
eve auth request-access --org "My Company" --email you@example.com
eve auth request-access --org "My Company" --ssh-key ~/.ssh/id_ed25519.pub
eve auth request-access --status <request_id>Admins approve or reject via:
bash
eve admin access-requests list
eve admin access-requests approve <request_id>
eve admin access-requests reject <request_id> --reason "..."List responses use the canonical envelope.
{ "data": [...] }Approval is atomic (single DB transaction) and idempotent -- re-approving a completed request returns the existing record. If the fingerprint is already registered, Eve reuses that identity owner. If a legacy partial org matches the requested slug and name, Eve reuses it during approval. Failed attempts never leave partial state.
无邀请的用户可申请访问:
bash
eve auth request-access --org "My Company" --email you@example.com
eve auth request-access --org "My Company" --ssh-key ~/.ssh/id_ed25519.pub
eve auth request-access --status <request_id>管理员通过以下命令批准或拒绝:
bash
eve admin access-requests list
eve admin access-requests approve <request_id>
eve admin access-requests reject <request_id> --reason "..."列表响应使用标准的格式。
{ "data": [...] }批准操作是原子性的(单数据库事务)且幂等——重新批准已完成的请求会返回现有记录。如果指纹已注册,Eve会复用该身份所有者。如果旧版部分组织与请求的slug和名称匹配,Eve会在批准期间复用它。失败的尝试不会留下部分状态。
Credential Check
凭证检查
Verify local AI tool credentials:
bash
eve auth creds # Show Claude + Codex cred status
eve auth creds --claude # Only Claude
eve auth creds --codex # Only CodexOutput includes token type ( or ), preview, and expiry. Use this to confirm token health before syncing.
setup-tokenoauth验证本地AI工具凭证:
bash
eve auth creds # 显示Claude + Codex凭证状态
eve auth creds --claude # 仅查看Claude
eve auth creds --codex # 仅查看Codex输出包含令牌类型(或)、预览和过期时间。在同步前使用此命令确认令牌状态。
setup-tokenoauthOAuth Token Sync
OAuth令牌同步
Sync local Claude/Codex OAuth tokens into Eve secrets so agents can use them. Scope precedence: project > org > user.
bash
eve auth sync # Sync to user-level (default)
eve auth sync --org org_xxx # Sync to org-level (shared across org projects)
eve auth sync --project proj_xxx # Sync to project-level (scoped to one project)
eve auth sync --dry-run # Preview without syncingThis sets / (Claude) and (Codex/Code) at the requested scope.
CLAUDE_CODE_OAUTH_TOKENCLAUDE_OAUTH_REFRESH_TOKENCODEX_AUTH_JSON_B64将本地Claude/Codex OAuth令牌同步到Eve密钥中,以便Agent使用。优先级:项目 > 组织 > 用户。
bash
eve auth sync # 默认同步到用户级别
eve auth sync --org org_xxx # 同步到组织级别(组织内所有项目共享)
eve auth sync --project proj_xxx # 同步到项目级别(限定到单个项目)
eve auth sync --dry-run # 预览同步内容,不执行实际操作此操作会在指定范围内设置/(Claude)和(Codex/Code)。
CLAUDE_CODE_OAUTH_TOKENCLAUDE_OAUTH_REFRESH_TOKENCODEX_AUTH_JSON_B64Claude Token Types
Claude令牌类型
| Token Prefix | Type | Lifetime | Recommendation |
|---|---|---|---|
| | Long-lived | Preferred for jobs and automation |
Other | | ~15 hours | Use for interactive dev; regenerate with |
eve auth synceve auth creds| 令牌前缀 | 类型 | 生命周期 | 推荐场景 |
|---|---|---|---|
| | 长期有效 | 作业和自动化优先选择 |
其他 | | ~15小时 | 用于交互式开发;使用 |
同步短期OAuth令牌时,会发出警告。同步前运行检查令牌类型。
eve auth synceve auth credsAutomatic Codex/Code Token Write-Back
Codex/Code令牌自动回写
After each harness invocation, the worker checks if the Codex/Code CLI refreshed during the session. If the token changed, it is automatically written back to the originating secret scope (user/org/project) so the next job starts with a fresh token. This is transparent and non-fatal -- a write-back failure logs a warning but does not affect the job result.
auth.jsonFor Codex/Code credentials, the sync picks the freshest token across and by comparing .
~/.codex/auth.json~/.code/auth.jsontokens.expires_at每次 harness 调用后,工作节点会检查Codex/Code CLI是否在会话期间刷新了。如果令牌已更改,会自动写回原始密钥范围(用户/组织/项目),以便下一个作业使用新鲜令牌。此操作是透明且非致命的——回写失败会记录警告,但不影响作业结果。
auth.json对于Codex/Code凭证,同步会通过比较,从和中选择最新的令牌。
tokens.expires_at~/.codex/auth.json~/.code/auth.jsonAccess Groups + Scoped Access
访问组 + 范围化访问
Groups are first-class authorization primitives that segment data-plane access (org filesystem, org docs, environment databases). Create groups, add members, and bind roles with scoped constraints:
bash
undefined组是一等授权原语,用于划分数据平面访问权限(组织文件系统、组织文档、环境数据库)。创建组、添加成员并绑定带有范围约束的角色:
bash
undefinedCreate a group
创建组
eve access groups create --org org_xxx --slug eng-team --name "Engineering"
eve access groups create --org org_xxx --slug eng-team --name "Engineering"
Add members
添加成员
eve access groups members add eng-team --org org_xxx --user user_abc
eve access groups members add eng-team --org org_xxx --service-principal sp_xxx
eve access groups members add eng-team --org org_xxx --user user_abc
eve access groups members add eng-team --org org_xxx --service-principal sp_xxx
Bind a role with scoped access
绑定带有范围化访问的角色
eve access bind --org org_xxx --group grp_xxx --role data-reader
--scope-json '{"orgfs":{"allow_prefixes":["/shared/"]},"envdb":{"schemas":["public"]}}'
--scope-json '{"orgfs":{"allow_prefixes":["/shared/"]},"envdb":{"schemas":["public"]}}'
eve access bind --org org_xxx --group grp_xxx --role data-reader
--scope-json '{"orgfs":{"allow_prefixes":["/shared/"]},"envdb":{"schemas":["public"]}}'
--scope-json '{"orgfs":{"allow_prefixes":["/shared/"]},"envdb":{"schemas":["public"]}}'
Check effective access
检查有效访问权限
eve access memberships --org org_xxx --user user_abc
undefinedeve access memberships --org org_xxx --user user_abc
undefinedScope Types
范围类型
| Resource | Scope Fields | Example |
|---|---|---|
| Org Filesystem | | |
| Org Documents | | |
| Environment DB | | |
| 资源 | 范围字段 | 示例 |
|---|---|---|
| 组织文件系统 | | |
| 组织文档 | | |
| 环境数据库 | | |
Group-Aware RLS
支持组的RLS
Scaffold RLS helper functions for group-based row-level security in environment databases:
bash
eve db rls init --with-groupsThis creates SQL helpers (, , ) that read session context set by Eve's runtime. Use them in RLS policies:
app.current_user_id()app.current_group_ids()app.has_group()sql
CREATE POLICY notes_group_read ON notes FOR SELECT
USING (group_id = ANY(app.current_group_ids()));为环境数据库中的基于组的行级安全搭建RLS辅助函数:
bash
eve db rls init --with-groups这会创建SQL辅助函数(、、),这些函数会读取Eve运行时设置的会话上下文。在RLS策略中使用它们:
app.current_user_id()app.current_group_ids()app.has_group()sql
CREATE POLICY notes_group_read ON notes FOR SELECT
USING (group_id = ANY(app.current_group_ids()));Membership Introspection
成员身份自省
Inspect a principal's full effective access -- base org/project roles, group memberships, resolved bindings, and merged scopes:
bash
eve access memberships --org org_xxx --user user_abc
eve access memberships --org org_xxx --service-principal sp_xxxThe response includes (merged across all bindings), , and each binding's (direct or group).
effective_scopeseffective_permissionsmatched_via查看主体的完整有效访问权限——基础组织/项目角色、组成员身份、已解析的绑定和合并后的范围:
bash
eve access memberships --org org_xxx --user user_abc
eve access memberships --org org_xxx --service-principal sp_xxx响应包含(所有绑定合并后的范围)、以及每个绑定的(直接或通过组匹配)。
effective_scopeseffective_permissionsmatched_viaResource-Specific Access Checks
特定资源的访问检查
Check and explain access against a specific data-plane resource:
bash
eve access can orgfs:read /shared/reports --org org_xxx
eve access explain orgfs:write /shared/reports --org org_xxx --user user_abcThe response includes , , and per-grant explaining why a binding did or did not match the requested resource path.
scope_requiredscope_matchedscope_reason检查并解释对特定数据平面资源的访问权限:
bash
eve access can orgfs:read /shared/reports --org org_xxx
eve access explain orgfs:write /shared/reports --org org_xxx --user user_abc响应包含、以及每个授权的,解释绑定为何匹配或不匹配请求的资源路径。
scope_requiredscope_matchedscope_reasonPolicy-as-Code (v2)
策略即代码(v2)
Declare groups, roles, and scoped bindings in . Use :
.eve/access.yamlversion: 2yaml
version: 2
access:
groups:
eng-team:
name: Engineering Team
description: Scoped access for engineering collaborators
members:
- type: user
id: user_abc
roles:
app_editor:
scope: org
permissions:
- orgdocs:read
- orgdocs:write
- orgfs:read
- envdb:read
bindings:
- subject: { type: group, id: eng-team }
roles: [app_editor]
scope:
orgdocs: { allow_prefixes: ["/groups/app/**"] }
orgfs: { allow_prefixes: ["/groups/app/**"] }
envdb: { schemas: ["app"] }Validate, plan, and sync:
bash
eve access validate --file .eve/access.yaml
eve access plan --file .eve/access.yaml --org org_xxx
eve access sync --file .eve/access.yaml --org org_xxxSync is declarative: it creates, updates, and prunes groups, members, roles, and bindings to match the YAML. Invalid scope configurations fail fast before any mutations are applied. Binding subjects can be , , or .
userservice_principalgroup在中声明组、角色和范围化绑定,使用:
.eve/access.yamlversion: 2yaml
version: 2
access:
groups:
eng-team:
name: Engineering Team
description: Scoped access for engineering collaborators
members:
- type: user
id: user_abc
roles:
app_editor:
scope: org
permissions:
- orgdocs:read
- orgdocs:write
- orgfs:read
- envdb:read
bindings:
- subject: { type: group, id: eng-team }
roles: [app_editor]
scope:
orgdocs: { allow_prefixes: ["/groups/app/**"] }
orgfs: { allow_prefixes: ["/groups/app/**"] }
envdb: { schemas: ["app"] }验证、规划并同步:
bash
eve access validate --file .eve/access.yaml
eve access plan --file .eve/access.yaml --org org_xxx
eve access sync --file .eve/access.yaml --org org_xxx同步是声明式的:它会创建、更新和删除组、成员、角色和绑定,以匹配YAML配置。无效的范围配置会在任何变更应用前快速失败。绑定主体可以是、或。
userservice_principalgroupKey Rotation
密钥轮换
Rotate the JWT signing key:
- Set alongside the existing secret
EVE_AUTH_JWT_SECRET_NEW - Server starts signing with the new key but accepts both during the grace period
- After grace period (), remove the old secret
EVE_AUTH_KEY_ROTATION_GRACE_HOURS - Emergency rotation: set only the new key (immediately invalidates all existing tokens)
轮换JWT签名密钥:
- 在现有密钥旁设置
EVE_AUTH_JWT_SECRET_NEW - 服务器开始使用新密钥签名,但在宽限期内仍接受旧密钥
- 宽限期过后(),移除旧密钥
EVE_AUTH_KEY_ROTATION_GRACE_HOURS - 紧急轮换:仅设置新密钥(立即使所有现有令牌失效)
App SSO Integration
应用SSO集成
Add Eve SSO login to any Eve-deployed app using two shared packages: (backend) and (frontend). The platform auto-injects , , , and into deployed services.
@eve-horizon/auth@eve-horizon/auth-reactEVE_SSO_URLEVE_ORG_IDEVE_PROJECT_IDEVE_API_URL使用两个共享包为任何Eve部署的应用添加Eve SSO登录:(后端)和(前端)。平台会自动将、、和注入到部署的服务中。
@eve-horizon/auth@eve-horizon/auth-reactEVE_SSO_URLEVE_ORG_IDEVE_PROJECT_IDEVE_API_URLMagic-Link Login Opt-In (Passwordless Apps)
魔法链接登录选择(无密码应用)
Apps can opt into passwordless browser login with . The SSO login page is branded for the project and shows email magic-link login instead of username/password.
x-eve.auth.login_method: magic_linkyaml
x-eve:
auth:
login_method: magic_link # or password_or_magic_link, password
self_signup: false # unknown emails get generic success, no email
invite_requires_password: false # invite callback skips /set-passwordMagic-link emails are sent by Eve API through (not GoTrue directly) so the platform can enforce project policy, share the template with invite emails, and avoid account enumeration. Projects without keep legacy SSO behavior. Create new users with .
POST /auth/magic-linkx-eve.brandingx-eve.autheve org invite <email> --org <org_id> --project <project_id>应用可通过选择无密码浏览器登录。SSO登录页面会使用项目品牌,并显示邮箱魔法链接登录而非用户名/密码。
x-eve.auth.login_method: magic_linkyaml
x-eve:
auth:
login_method: magic_link # 或 password_or_magic_link, password
self_signup: false # 未知邮箱会返回通用成功,不发送邮件
invite_requires_password: false # 邀请回调跳过/set-password页面魔法链接邮件由Eve API通过发送(而非直接通过GoTrue),以便平台执行项目策略,与邀请邮件共享模板,并避免账户枚举。未设置的项目保留旧版SSO行为。使用创建新用户。
POST /auth/magic-linkx-eve.brandingx-eve.autheve org invite <email> --org <org_id> --project <project_id>Magic-Link Confirmation Interstitial (Security)
魔法链接确认中间页(安全措施)
Eve-rendered magic-link and invite emails embed a wrap URL (), not the raw GoTrue verify URL. Email-security scanners (Defender SafeLinks, Mimecast, Proofpoint, Barracuda) follow every URL in mail and would otherwise consume single-use OTPs before the human clicks. The wrap renders a branded "Confirm sign-in / Accept invite" page; only the POST from the button reveals the GoTrue URL and 302-redirects. Treat this as a platform guarantee — no app-side work required.
https://sso/m/mlw_<id>Eve渲染的魔法链接和邀请邮件嵌入包装URL(),而非原始GoTrue验证URL。邮件安全扫描器(Defender SafeLinks、Mimecast、Proofpoint、Barracuda)会跟踪邮件中的每个URL,否则会在用户点击前消耗一次性OTP。包装页面会显示品牌化的"确认登录/接受邀请"页面;只有按钮的POST请求会暴露GoTrue URL并通过302重定向。这是平台提供的保障——无需应用端做任何工作。
https://sso/m/mlw_<id>Domain-Based Signup (Path C Auto-Attach)
基于域名的注册(Path C自动附加)
Pre-approve email domains so anyone with a matching address can sign in via magic link without a per-user invite. On first successful login the platform attaches them as of the rule's . One project can route different domains to different orgs.
membertarget_orgyaml
x-eve:
auth:
login_method: magic_link
invite_requires_password: false
org_access:
mode: allowlist
allowed_orgs: [org_Acme, org_Partner, org_Retailer]
domain_signup:
enabled: true
domains:
- { domain: example.com, target_org: org_Acme, role: member }
- { domain: partner.example, target_org: org_Partner }
- { domain: retailer.example, target_org: org_Retailer }Rules are walked in declaration order — first match wins, so declare more-specific patterns first. Each rule's must appear in . Declaring free-email providers () is allowed but produces a manifest coherence warning. Explicit pending invites take priority over domain signup (Path B beats Path C). Removing a rule stops new signups but does not retroactively remove existing memberships — drop those with .
target_orgallowed_orgsfree-mail.exampleeve org members removeAudit via the event spine: and carry , , and .
auth.domain_signup.invite_createdauth.domain_signup.member_attachedorg_idmatched_rulematched_domain预先批准邮箱域名,使任何匹配地址的用户都可通过魔法链接登录,无需逐个用户邀请。首次登录成功后,平台会将他们作为规则的附加。一个项目可将不同域名路由到不同组织。
target_orgmemberyaml
x-eve:
auth:
login_method: magic_link
invite_requires_password: false
org_access:
mode: allowlist
allowed_orgs: [org_Acme, org_Partner, org_Retailer]
domain_signup:
enabled: true
domains:
- { domain: example.com, target_org: org_Acme, role: member }
- { domain: partner.example, target_org: org_Partner }
- { domain: retailer.example, target_org: org_Retailer }规则按声明顺序匹配——第一个匹配项生效,因此先声明更具体的模式。每个规则的必须出现在中。声明免费邮箱提供商()是允许的,但会产生清单一致性警告。明确的待处理邀请优先于域名注册(Path B优于Path C)。移除规则会停止新注册,但不会追溯移除现有成员身份——使用删除。
target_orgallowed_orgsfree-mail.exampleeve org members remove通过事件 spine 审计:和包含、和。
auth.domain_signup.invite_createdauth.domain_signup.member_attachedorg_idmatched_rulematched_domainApp Org Access and Admin Invites
应用组织访问和管理员邀请
Apps default to project-owner-org access. Use to declare which customer orgs may use the app, and enable in-app admin invites that send branded magic-link onboarding:
mode: allowlistyaml
x-eve:
auth:
org_access:
mode: allowlist
allowed_orgs: [org_customer123, customer-slug]
invite:
enabled: true
admin_roles: [admin, owner]
invited_role: member # fixed; app invites cannot create adminsEndpoints: returns the user's allowed orgs (plus which ones they can invite into); lets an org admin/owner invite a regular member with the project-branded email. For cross-org apps, use on the backend instead of — it consults and selects the org from , , or first allowed.
GET /auth/app-accessPOST /auth/app-inviteseveAppUserAuth()eveUserAuth()/auth/app-accessX-Eve-Org-Id?eve_org_id=应用默认访问项目所属组织。使用声明哪些客户组织可以使用应用,并启用应用内管理员邀请,发送品牌化魔法链接入职邮件:
mode: allowlistyaml
x-eve:
auth:
org_access:
mode: allowlist
allowed_orgs: [org_customer123, customer-slug]
invite:
enabled: true
admin_roles: [admin, owner]
invited_role: member # 固定;应用邀请无法创建管理员端点:返回用户允许访问的组织(以及他们可以邀请的组织);允许组织管理员/所有者使用项目品牌化邮件邀请普通成员。对于跨组织应用,在后端使用而非——它会查询并从、或第一个允许的组织中选择组织。
GET /auth/app-accessPOST /auth/app-inviteseveAppUserAuth()eveUserAuth()/auth/app-accessX-Eve-Org-Id?eve_org_id=Project-Scoped Redirect Allowlist (Custom-Domain Apps)
项目范围的重定向允许列表(自定义域名应用)
The SSO broker only accepts redirect targets under the cluster domain by default. Apps deployed on their own domain must declare their origins:
yaml
x-eve:
auth:
allowed_redirect_origins:
- https://app.example.com
- https://www.example.comEntries are origin-only (); paths/queries/fragments are rejected at manifest-validate time. The final allowlist returned by is the union of: (1) explicit manifest entries, (2) the project's own eligible custom domains ( rows with and status //), and (3) cross-org custom domains owned by projects in . Inspect with .
scheme://host[:port]GET /auth/app-contextcustom_domainsenvironment_iddns_verifiedcert_provisioningactiveallowed_orgseve project auth-context <project_id>This replaces the hard-coded allowlist for non-cluster origins. The broker uses the list for both validation in and CORS on and . The provider auto-passes on session/logout calls so cross-site cookies are scoped correctly.
EVE_DEFAULT_DOMAINredirect_to/callback/session/logout@eve-horizon/auth-reactproject_id默认情况下,SSO代理仅接受集群域名下的重定向目标。部署在自有域名上的应用必须声明其源:
yaml
x-eve:
auth:
allowed_redirect_origins:
- https://app.example.com
- https://www.example.com条目仅包含源();路径/查询/片段会在清单验证时被拒绝。返回的最终允许列表是以下内容的并集:(1) 显式清单条目,(2) 项目自身符合条件的自定义域名(行中带有且状态为//),以及(3) 中项目拥有的跨组织自定义域名。使用查看。
scheme://host[:port]GET /auth/app-contextcustom_domainsenvironment_iddns_verifiedcert_provisioningactiveallowed_orgseve project auth-context <project_id>这取代了针对非集群源的硬编码允许列表。代理会将此列表用于中的验证以及和上的CORS。提供商会在会话/注销调用时自动传递,以便跨站点cookie正确作用域。
EVE_DEFAULT_DOMAIN/callbackredirect_to/session/logout@eve-horizon/auth-reactproject_idSameSite=None on Custom Domains (Platform Guarantee)
自定义域名上的SameSite=None(平台保障)
When SSO is deployed with , the broker emits and cookies with . This is required for the React provider's cross-site probe to carry cookies when the app is on a custom domain. Local k3d () stays on . Apps no longer need to configure this themselves.
EVE_SSO_SECURE_COOKIES=trueeve_sso_rteve_ssoSecure; SameSite=Nonefetch('/session', { credentials: 'include' })http://*.lvh.meSameSite=Lax当SSO部署时设置,代理会发出带有的和 cookie。这是React提供商跨站点探测在应用位于自定义域名时携带cookie所必需的。本地k3d()保持。应用无需自行配置此项。
EVE_SSO_SECURE_COOKIES=trueSecure; SameSite=Noneeve_sso_rteve_ssofetch('/session', { credentials: 'include' })http://*.lvh.meSameSite=LaxRestrict Self-Signup to Approved Email Domains
将自助注册限制为批准的邮箱域名
The SSO service gates and by email domain when the env var is set (comma-separated). Unset means all domains are allowed (default). The signup tab on the SSO login page displays a domain hint when restrictions are active.
/auth/signup/auth/magiclinkEVE_SIGNUP_ALLOWED_EMAIL_DOMAINSbash
undefined当设置环境变量(逗号分隔)时,SSO服务会限制和的邮箱域名。未设置则允许所有域名(默认)。当限制生效时,SSO登录页面的注册标签会显示域名提示。
EVE_SIGNUP_ALLOWED_EMAIL_DOMAINS/auth/signup/auth/magiclinkbash
undefinedOn the SSO deployment, set:
在SSO部署中设置:
EVE_SIGNUP_ALLOWED_EMAIL_DOMAINS=acme.com,partner.io
Rejected requests return HTTP 422 with `error: email_domain_not_allowed`. Use this to keep public SSO endpoints invite-only-by-domain without disabling self-signup entirely. Existing accounts and admin invites are unaffected.EVE_SIGNUP_ALLOWED_EMAIL_DOMAINS=acme.com,partner.io
被拒绝的请求会返回HTTP 422,包含`error: email_domain_not_allowed`。使用此功能可保持公共SSO端点按域名邀请,而无需完全禁用自助注册。现有账户和管理员邀请不受影响。Backend (@eve-horizon/auth
)
@eve-horizon/auth后端(@eve-horizon/auth
)
@eve-horizon/authInstall:
npm install @eve-horizon/authUse the unified middleware by default for new apps:
| Export | Behavior |
|---|---|
| Non-blocking middleware. Verifies user or agent tokens and attaches normalized identity at |
| Returns 401 if |
| Handler returning |
| |
Keep the legacy split middleware only for apps that explicitly want user-only or agent-only handling:
| Export | Behavior |
|---|---|
| User-only non-blocking middleware. Attaches |
| Returns 401 if |
| Blocking middleware for agent/job tokens. Attaches |
| JWKS-based local verification (15-min cache). Returns |
| HTTP verification via |
Express setup (~3 lines):
typescript
import { eveAuth, eveIdentityGuard, eveAuthConfig, eveAuthMe } from '@eve-horizon/auth';
app.use(eveAuth());
app.get('/auth/config', eveAuthConfig());
app.get('/auth/me', eveAuthMe()); // Full response for React SDK
app.use('/api', eveIdentityGuard());req.eveIdentity- User token: ,
id,email,orgId,role,permissionsisAgent: false - Agent/job token: ,
jobId, stableagentSlugasemail,{agent_slug}@eve.agent,permissionsisAgent: true
Use or the stable agent email for RLS, audit logs, and app-level routing. Do not key agent identity off ; that older pattern was per-job and unstable.
agentSlug{job_id}@eve.agentNestJS setup: apply globally in , then use a thin guard wrapper:
eveAuth()main.tstypescript
// main.ts
import { eveAuth } from '@eve-horizon/auth';
app.use(eveAuth());
// auth.guard.ts -- thin NestJS adapter
@Injectable()
export class EveGuard implements CanActivate {
canActivate(ctx: ExecutionContext): boolean {
const req = ctx.switchToHttp().getRequest();
if (!req.eveIdentity) throw new UnauthorizedException();
return true;
}
}
// auth-config.controller.ts
@Controller()
export class AuthConfigController {
private handler = eveAuthConfig();
@Get('auth/config')
getConfig(@Req() req, @Res() res) { this.handler(req, res); }
}Verification strategies: and default to (JWKS, cached 15 min). Use for immediate membership freshness at ~50ms latency per request.
eveAuth()eveUserAuth()'local'strategy: 'remote'Custom role mapping: If your app needs roles beyond Eve's , bridge after :
owner/admin/membereveAuth()typescript
app.use((req, _res, next) => {
if (req.eveIdentity && !req.eveIdentity.isAgent) {
req.user = {
...req.eveIdentity,
appRole: req.eveIdentity.role === 'member' ? 'viewer' : 'admin',
};
}
next();
});安装:
npm install @eve-horizon/auth默认使用统一中间件用于新应用:
| 导出项 | 行为 |
|---|---|
| 非阻塞中间件。验证用户或Agent令牌,并将标准化身份附加到 |
| 如果 |
| 处理程序,从自动注入的环境变量返回 |
| |
仅对于明确需要仅用户或仅Agent处理的应用,保留旧版拆分中间件:
| 导出项 | 行为 |
|---|---|
| 仅用户的非阻塞中间件。将 |
| 如果 |
| 针对Agent/作业令牌的阻塞中间件。将带有完整 |
| 基于JWKS的本地验证(缓存15分钟)。返回 |
| 通过 |
Express设置(约3行):
typescript
import { eveAuth, eveIdentityGuard, eveAuthConfig, eveAuthMe } from '@eve-horizon/auth';
app.use(eveAuth());
app.get('/auth/config', eveAuthConfig());
app.get('/auth/me', eveAuthMe()); // 供React SDK使用的完整响应
app.use('/api', eveIdentityGuard());req.eveIdentity- 用户令牌:、
id、email、orgId、role、permissionsisAgent: false - Agent/作业令牌:、
jobId、稳定的agentSlug格式为email、{agent_slug}@eve.agent、permissionsisAgent: true
使用或稳定的Agent邮箱用于RLS、审计日志和应用级路由。不要使用作为Agent身份标识;旧模式是每个作业的,不稳定。
agentSlug{job_id}@eve.agentNestJS设置:在中全局应用,然后使用轻量防护包装器:
main.tseveAuth()typescript
// main.ts
import { eveAuth } from '@eve-horizon/auth';
app.use(eveAuth());
// auth.guard.ts -- 轻量NestJS适配器
@Injectable()
export class EveGuard implements CanActivate {
canActivate(ctx: ExecutionContext): boolean {
const req = ctx.switchToHttp().getRequest();
if (!req.eveIdentity) throw new UnauthorizedException();
return true;
}
}
// auth-config.controller.ts
@Controller()
export class AuthConfigController {
private handler = eveAuthConfig();
@Get('auth/config')
getConfig(@Req() req, @Res() res) { this.handler(req, res); }
}验证策略:和默认使用(JWKS,缓存15分钟)。使用可立即获取成员身份最新状态,每次请求延迟约50ms。
eveAuth()eveUserAuth()'local'strategy: 'remote'自定义角色映射:如果应用需要Eve的之外的角色,在之后进行桥接:
owner/admin/membereveAuth()typescript
app.use((req, _res, next) => {
if (req.eveIdentity && !req.eveIdentity.isAgent) {
req.user = {
...req.eveIdentity,
appRole: req.eveIdentity.role === 'member' ? 'viewer' : 'admin',
};
}
next();
});Frontend (@eve-horizon/auth-react
)
@eve-horizon/auth-react前端(@eve-horizon/auth-react
)
@eve-horizon/auth-reactInstall:
npm install @eve-horizon/auth-react| Export | Purpose |
|---|---|
| Context provider. Bootstraps session: checks sessionStorage, probes SSO |
| Hook: |
| Renders children when authenticated, login form otherwise. |
| Built-in SSO + token-paste login UI. |
| Fetch wrapper with automatic Bearer injection. |
Simple setup -- handles the loading/login/authenticated states:
EveLoginGatetsx
import { EveAuthProvider, EveLoginGate } from '@eve-horizon/auth-react';
<EveAuthProvider apiUrl="/api">
<EveLoginGate>
<ProtectedApp />
</EveLoginGate>
</EveAuthProvider>Custom auth gate -- use for full control over loading, login, and error states:
useEveAuth()tsx
import { EveAuthProvider, useEveAuth } from '@eve-horizon/auth-react';
function AuthGate() {
const { user, loading, loginWithToken, loginWithSso, logout } = useEveAuth();
if (loading) return <Spinner />;
if (!user) return <LoginPage onSso={loginWithSso} onToken={loginWithToken} />;
return <App user={user} onLogout={logout} />;
}
export default () => (
<EveAuthProvider apiUrl="/api">
<AuthGate />
</EveAuthProvider>
);API calls with auth: Use for automatic Bearer token injection:
createEveClient()typescript
import { createEveClient } from '@eve-horizon/auth-react';
const client = createEveClient('/api');
const res = await client.fetch('/data');安装:
npm install @eve-horizon/auth-react| 导出项 | 用途 |
|---|---|
| 上下文提供程序。引导会话:检查sessionStorage,探测SSO |
| Hook: |
| 已认证时渲染子组件,否则显示登录表单。 |
| 内置SSO + 令牌粘贴登录UI。 |
| Fetch包装器,自动注入Bearer令牌。 |
简单设置——处理加载/登录/已认证状态:
EveLoginGatetsx
import { EveAuthProvider, EveLoginGate } from '@eve-horizon/auth-react';
<EveAuthProvider apiUrl="/api">
<EveLoginGate>
<ProtectedApp />
</EveLoginGate>
</EveAuthProvider>自定义认证门——使用完全控制加载、登录和错误状态:
useEveAuth()tsx
import { EveAuthProvider, useEveAuth } from '@eve-horizon/auth-react';
function AuthGate() {
const { user, loading, loginWithToken, loginWithSso, logout } = useEveAuth();
if (loading) return <Spinner />;
if (!user) return <LoginPage onSso={loginWithSso} onToken={loginWithToken} />;
return <App user={user} onLogout={logout} />;
}
export default () => (
<EveAuthProvider apiUrl="/api">
<AuthGate />
</EveAuthProvider>
);带认证的API调用:使用自动注入Bearer令牌:
createEveClient()typescript
import { createEveClient } from '@eve-horizon/auth-react';
const client = createEveClient('/api');
const res = await client.fetch('/data');Migration from Custom Auth
从自定义认证迁移
The SDK replaces ~700-800 lines of hand-rolled auth with ~50 lines. Delete custom JWKS/token verification, Bearer extraction middleware, SSO URL discovery, session probe logic, token storage helpers, and login form. Keep app-specific role mapping and local password auth.
For the full migration checklist, types reference, token lifecycle, and advanced patterns (SSE auth, token paste mode, token staleness), see references/app-sso-integration.md.
SDK用约50行代码取代了约700-800行手动编写的认证代码。删除自定义JWKS/令牌验证、Bearer提取中间件、SSO URL发现、会话探测逻辑、令牌存储助手和登录表单。保留应用特定的角色映射和本地密码认证。
有关完整的迁移清单、类型参考、令牌生命周期和高级模式(SSE认证、令牌粘贴模式、令牌过期),请参阅references/app-sso-integration.md。
Service Tokens for Deployed Services
部署服务的服务令牌
Every deployed service receives an auto-injected (90-day RS256 JWT, ) for server-to-server calls back into the Eve API. The deployer mints it on each deploy — apps no longer need to manually set this secret.
EVE_SERVICE_TOKENtype: serviceTokens default to read-only permissions (, , , , , , , , ). Apps that need write access declare additional permissions explicitly in the manifest:
projects:readjobs:readthreads:readenvs:readsecrets:readbuilds:readpipelines:readagents:readevents:readyaml
services:
api:
x-eve:
permissions: [jobs:write, events:write, threads:write]Use this for app -> Eve API calls (creating jobs, emitting events, updating threads). For the full schema and call patterns, see eve-read-eve-docs/references/secrets-auth.md and eve-read-eve-docs/references/manifest.md.
每个部署的服务都会收到自动注入的(90天RS256 JWT,),用于服务器到服务器调用Eve API。部署程序会在每次部署时生成它——应用无需手动设置此密钥。
EVE_SERVICE_TOKENtype: service令牌默认拥有只读权限(、、、、、、、、)。需要写入权限的应用在清单中显式声明额外权限:
projects:readjobs:readthreads:readenvs:readsecrets:readbuilds:readpipelines:readagents:readevents:readyaml
services:
api:
x-eve:
permissions: [jobs:write, events:write, threads:write]用于应用 -> Eve API调用(创建作业、发送事件、更新线程)。有关完整模式和调用模式,请参阅eve-read-eve-docs/references/secrets-auth.md和eve-read-eve-docs/references/manifest.md。
BYOK Model (LLM API Keys)
BYOK模型(LLM API密钥)
Eve does not proxy inference traffic. All model access is BYOK (Bring Your Own Keys): harnesses and apps bring their own API keys via secrets and call providers directly.
Store LLM provider keys as project secrets:
bash
eve secrets set ANTHROPIC_API_KEY "sk-ant-xxx" --project proj_xxx
eve secrets set OPENAI_API_KEY "sk-xxx" --project proj_xxx
eve secrets set OPENAI_BASE_URL "https://my-vllm.runpod.ai/v1" --project proj_xxxHarnesses resolve these automatically. For self-hosted models (vLLM, LM Studio via Tailscale), set the base URL and API key as secrets -- Eve provides connectivity via private endpoints (see ), not a managed inference layer.
eve-deploy-debuggingEve不代理推理流量。所有模型访问均采用BYOK(自带密钥)模式:harness和应用通过密钥携带自己的API密钥,直接调用提供商。
将LLM提供商密钥存储为项目密钥:
bash
eve secrets set ANTHROPIC_API_KEY "sk-ant-xxx" --project proj_xxx
eve secrets set OPENAI_API_KEY "sk-xxx" --project proj_xxx
eve secrets set OPENAI_BASE_URL "https://my-vllm.runpod.ai/v1" --project proj_xxxHarness会自动解析这些密钥。对于自托管模型(vLLM、通过Tailscale的LM Studio),将基础URL和API密钥设置为密钥——Eve通过私有端点提供连接(参见),而非托管推理层。
eve-deploy-debuggingPer-Org OAuth Credentials (BYOA)
每个组织的OAuth凭证(BYOA)
Each org brings its own OAuth app credentials for Google Drive, Slack, and other integrations. No cluster-level shared secrets.
bash
undefined每个组织都为Google Drive、Slack和其他集成带来自己的OAuth应用凭证。无集群级共享密钥。
bash
undefinedView setup instructions (redirect URIs, required scopes)
查看设置说明(重定向URI、所需范围)
eve integrations setup-info google-drive
eve integrations setup-info slack
eve integrations setup-info google-drive
eve integrations setup-info slack
Register OAuth app credentials
注册OAuth应用凭证
eve integrations configure google-drive
--client-id "xxx.apps.googleusercontent.com"
--client-secret "GOCSPX-xxx"
--label "Acme Corp Google Drive"
--client-id "xxx.apps.googleusercontent.com"
--client-secret "GOCSPX-xxx"
--label "Acme Corp Google Drive"
eve integrations configure slack
--client-id "12345.67890"
--client-secret "abc123"
--signing-secret "def456"
--app-id "A0123ABC"
--label "Acme Corp Slack Bot"
--client-id "12345.67890"
--client-secret "abc123"
--signing-secret "def456"
--app-id "A0123ABC"
--label "Acme Corp Slack Bot"
eve integrations configure google-drive
--client-id "xxx.apps.googleusercontent.com"
--client-secret "GOCSPX-xxx"
--label "Acme Corp Google Drive"
--client-id "xxx.apps.googleusercontent.com"
--client-secret "GOCSPX-xxx"
--label "Acme Corp Google Drive"
eve integrations configure slack
--client-id "12345.67890"
--client-secret "abc123"
--signing-secret "def456"
--app-id "A0123ABC"
--label "Acme Corp Slack Bot"
--client-id "12345.67890"
--client-secret "abc123"
--signing-secret "def456"
--app-id "A0123ABC"
--label "Acme Corp Slack Bot"
View current config (secrets redacted)
查看当前配置(密钥已脱敏)
eve integrations config google-drive
eve integrations config google-drive
Then connect as before (uses per-org credentials)
然后像之前一样连接(使用每个组织的凭证)
eve integrations connect google-drive
eve integrations connect slack
Benefits: isolated credentials per org, custom consent screen branding, independent rate limits, no shared-secret blast radius.eve integrations connect google-drive
eve integrations connect slack
优势:每个组织的凭证隔离、自定义同意屏幕品牌、独立速率限制、无共享密钥爆炸半径。Project Role Resolution
项目角色解析
Role and org membership changes take effect immediately -- the server resolves permissions from live DB memberships, not stale JWT claims. When a request includes a but no , the permission guard derives the org context from the project's owning org.
project_idorg_idThe Auth SDK () exposes this via middleware. Use for immediate membership freshness when needed.
@eve-horizon/autheveUserAuth()strategy: 'remote'角色和组织成员身份变更会立即生效——服务器从实时数据库成员关系解析权限,而非过期的JWT声明。当请求包含但无时,权限防护会从项目所属组织推导组织上下文。
project_idorg_idAuth SDK()通过中间件暴露此功能。必要时使用获取即时成员身份最新状态。
@eve-horizon/autheveUserAuth()strategy: 'remote'Project Secrets
项目密钥
bash
undefinedbash
undefinedSet a secret
设置密钥
eve secrets set API_KEY "your-api-key" --project proj_xxx
eve secrets set API_KEY "your-api-key" --project proj_xxx
List keys (no values)
列出密钥(无值)
eve secrets list --project proj_xxx
eve secrets list --project proj_xxx
Delete a secret
删除密钥
eve secrets delete API_KEY --project proj_xxx
eve secrets delete API_KEY --project proj_xxx
Import from file
从文件导入
eve secrets import .env --project proj_xxx
undefinedeve secrets import .env --project proj_xxx
undefinedSecret Interpolation
密钥插值
Reference secrets in using :
.eve/manifest.yaml${secret.KEY}yaml
services:
api:
environment:
API_KEY: ${secret.API_KEY}在中使用引用密钥:
.eve/manifest.yaml${secret.KEY}yaml
services:
api:
environment:
API_KEY: ${secret.API_KEY}Manifest Validation
清单验证
Validate that all required secrets are set before deploying:
bash
eve manifest validate --validate-secrets # check secret references
eve manifest validate --strict # fail on missing secrets部署前验证所有必需密钥已设置:
bash
eve manifest validate --validate-secrets # 检查密钥引用
eve manifest validate --strict # 缺少密钥则失败Local Secrets File
本地密钥文件
For local development, create (gitignored):
.eve/dev-secrets.yamlyaml
secrets:
default:
API_KEY: local-dev-key
DB_PASSWORD: local-password
staging:
DB_PASSWORD: staging-password对于本地开发,创建(已加入git忽略):
.eve/dev-secrets.yamlyaml
secrets:
default:
API_KEY: local-dev-key
DB_PASSWORD: local-password
staging:
DB_PASSWORD: staging-passwordWorker Injection
工作节点注入
At job execution time, resolved secrets are injected as environment variables into the worker container. File-type secrets are written to disk and referenced via . The file is removed after the agent process reads it.
EVE_SECRETS_FILE作业执行时,解析后的密钥会作为环境变量注入到工作节点容器中。文件类型密钥会写入磁盘,并通过引用。Agent进程读取后会删除该文件。
EVE_SECRETS_FILEGit Auth
Git认证
The worker uses secrets for repository access:
- HTTPS: secret →
github_tokenheaderAuthorization: Bearer - SSH: secret → written to
ssh_keyand used via~/.ssh/GIT_SSH_COMMAND
工作节点使用密钥访问仓库:
- HTTPS:密钥 →
github_token头Authorization: Bearer - SSH:密钥 → 写入
ssh_key并通过~/.ssh/使用GIT_SSH_COMMAND
Auth Mail Delivery (SES)
认证邮件投递(SES)
All branded auth emails (org/app invites, app-scoped magic-link, system-admin Supabase invites) flow through a single . When SMTP points at SES ( or ), the mailer adds a pre-flight check so account-level suppressions cannot silently look like a successful send.
MailerServiceGOTRUE_SMTP_HOST=*.amazonaws.comEVE_MAILER_CHECK_SUPPRESSION=trueGetSuppressedDestination| Outcome | Behavior |
|---|---|
| Address suppressed | Throws |
| Not found | Send proceeds |
| AWS error (IRSA, throttling, network) | Fails open — logs |
Caller behavior: swallows and returns generic success (preserves account-enumeration defense), logging . Invite paths re-throw so admins see the error.
sendEligibleMagicLinkEmailSuppressedErrormail.suppressed_dropWhen is set, SES routes Bounce/Complaint/Delivery/Reject events to SNS, which POSTs to . The webhook verifies SNS signature, checks against , and persists one row per affected recipient in (idempotent by ).
EVE_SES_CONFIGURATION_SET/webhooks/ses-feedbackTopicArnEVE_SES_FEEDBACK_TOPIC_ARNemail_delivery_eventssha256(snsMessageId|eventType|recipient)Inspect events via the admin CLI:
bash
eve admin email bounces list
eve admin email bounces list --recipient user@example.com
eve admin email bounces list --event-type Bounce --limit 100 --jsonRead-only from the local table; does not mutate SES. To clear an account-level suppression, see the SES suppression runbook.
Structured log events to grep in API pod logs: , , , , , , , .
mailer.sentmailer.smtp_failedmailer.suppressedmailer.suppression_check_failedmail.suppressed_dropsns.subscription_confirmedsns.rejectedses.feedback_persisted所有品牌化认证邮件(组织/应用邀请、应用范围的魔法链接、系统管理员Supabase邀请)都通过单个发送。当SMTP指向SES(或)时,邮件服务会添加预飞行检查,以便账户级抑制不会被误认为发送成功。
MailerServiceGOTRUE_SMTP_HOST=*.amazonaws.comEVE_MAILER_CHECK_SUPPRESSION=trueGetSuppressedDestination| 结果 | 行为 |
|---|---|
| 地址被抑制 | 抛出 |
| 未找到 | 继续发送 |
| AWS错误(IRSA、限流、网络) | 开放失败——记录 |
调用者行为:会吞掉并返回通用成功(保留账户枚举防御),记录。邀请路径会重新抛出错误,以便管理员看到。
sendEligibleMagicLinkEmailSuppressedErrormail.suppressed_drop当设置时,SES会将退回/投诉/投递/拒绝事件路由到SNS,SNS会POST到。Webhook会验证SNS签名,检查是否与匹配,并在中为每个受影响的收件人持久化一行记录(通过实现幂等)。
EVE_SES_CONFIGURATION_SET/webhooks/ses-feedbackTopicArnEVE_SES_FEEDBACK_TOPIC_ARNemail_delivery_eventssha256(snsMessageId|eventType|recipient)通过管理员CLI查看事件:
bash
eve admin email bounces list
eve admin email bounces list --recipient user@example.com
eve admin email bounces list --event-type Bounce --limit 100 --json从本地表只读访问;不修改SES。要清除账户级抑制,请参阅SES抑制运行手册。
在API pod日志中可 grep 的结构化日志事件:、、、、、、、。
mailer.sentmailer.smtp_failedmailer.suppressedmailer.suppression_check_failedmail.suppressed_dropsns.subscription_confirmedsns.rejectedses.feedback_persistedTroubleshooting
故障排除
| Problem | Fix |
|---|---|
| Not authenticated | Run |
| Token expired | Re-run |
| Bootstrap already completed | Use |
| Secret missing | Confirm with |
| Interpolation error | Verify |
| Git clone failed | Check |
| Service can't reach API | Verify |
| Scoped access denied | Run |
| Wrong role shown | Role is resolved from live DB memberships. Run |
| Short-lived Claude token in jobs | Run |
| Codex token expired between jobs | Automatic write-back should refresh it. If not, re-run |
| App SSO not working | Verify |
| Stale org membership in app tokens | Default 1-day TTL. Use |
| 问题 | 解决方法 |
|---|---|
| 未认证 | 运行 |
| 令牌过期 | 重新运行 |
| 引导已完成 | 使用 |
| 密钥缺失 | 使用 |
| 插值错误 | 验证 |
| Git克隆失败 | 检查 |
| 服务无法访问API | 验证 |
| 范围化访问被拒绝 | 运行 |
| 显示错误角色 | 角色从实时数据库成员关系解析。运行 |
| 作业中使用短期Claude令牌 | 运行 |
| 作业间Codex令牌过期 | 自动回写应刷新它。如果没有,重新运行 |
| 应用SSO不工作 | 验证 |
| 应用令牌中的组织成员身份过期 | 默认1天有效期。在 |
Incident Response (Secret Leak)
事件响应(密钥泄露)
If a secret may be compromised:
- Contain: Rotate the secret immediately via
eve secrets set - Invalidate: Redeploy affected environments
- Audit: Check for recent jobs that used the secret
eve job list - Recover: Generate new credentials at the source (GitHub, AWS, etc.)
- Document: Record the incident and update rotation procedures
如果密钥可能已泄露:
- 遏制:立即通过轮换密钥
eve secrets set - 失效:重新部署受影响的环境
- 审计:检查查看最近使用该密钥的作业
eve job list - 恢复:在源端生成新凭证(GitHub、AWS等)
- 记录:记录事件并更新轮换流程