eve-fullstack-app-design について
eve-fullstack-app-designは、Eve Horizon上でアプリを設計または移行するチーム向けのアーキテクチャスキルです。Eveのマニフェストを軸に、サービス、インフラ、データベース、デプロイを計画するパターンを示します。
主な機能
- 01マニフェスト中心のアプリ設計
- 02サービストポロジーと ingress の計画
- 03マネージド・外部サービスの指針
- 04データベースとマイグレーションのパターン
- 05SPA・API・DBの参照構成
活用シーン
- 01新しいEveアプリの設計
- 02既存アプリのEveへの移行
- 03サービスとデータベース構成の選定
- 04デプロイパイプラインと環境の設計
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ incept5/ eve-skillpacks --skill eve-fullstack-app-designインストール後、Claude Code で/eve-fullstack-app-designを入力して読み込まれたか確認します。
詳細
Eve Horizon でのフルスタックアプリ設計
マニフェストを設計図とし、プラットフォームがインフラストラクチャを担い、あらゆる設計上の判断に意図を持たせてアプリケーションを設計します。
使用するタイミング
次のような場合に、このスキルを読み込みます。
- Eve 上で新しいアプリケーションをゼロから設計する場合
- 既存のアプリをプラットフォームに移行する場合
- 現在のアーキテクチャが Eve の機能をうまく活用しているか評価する場合
- サービスのトポロジー、データベース戦略、デプロイパイプラインを計画する場合
- マネージドサービスと外部サービスのどちらを選ぶか判断する場合
このスキルでは、Eve の PaaS レイヤーに関する 設計の考え方 を学びます。CLI の使用方法や運用の詳細については、対応する eve-se スキル(eve-manifest-authoring、eve-deploy-debugging、eve-auth-and-secrets、eve-pipelines-workflows)を読み込みます。
設計図としてのマニフェスト
マニフェスト(.eve/manifest.yaml)は、アプリケーションの構成を定義する唯一の信頼できる情報源です。単なる設定ではなく、アーキテクチャ文書として扱ってください。
マニフェストで宣言する内容
| 項目 | マニフェストのセクション | 設計上の判断 |
|---|---|---|
| サービストポロジー | services | 実行するプロセスと、その接続方法 |
| インフラストラクチャ | services[].x-eve | マネージド DB、イングレス、ロール |
| ビルド戦略 | services[].build + registry | ビルド対象とイメージの格納先 |
| リリースパイプライン | pipelines | コミットから本番環境までコードをどのように流すか |
| 環境の構成 | environments | 存在する環境と、それぞれが使用するパイプライン |
| エージェント設定 | x-eve.agents、x-eve.chat | エージェントのプロファイル、チームへの振り分け、チャットのルーティング |
| ランタイムのデフォルト | x-eve.defaults | ハーネス、ワークスペース、Git ポリシー |
設計原則:エージェントやオペレーターがマニフェストを読むだけでアプリの構成を理解できないなら、そのマニフェストは不完全です。
サービストポロジー
サービスを選ぶ
Eve アプリの多くは、次のいずれかのパターンに従います。
API + データベース(最もシンプル):
services:
api: # HTTP service with ingress
db: # managed Postgres
API + ワーカー + データベース:
services:
api: # HTTP service (user-facing)
worker: # Background processor (jobs, queues)
db: # managed Postgres
マルチサービス:
services:
web: # Frontend/SSR
api: # Backend API
worker: # Background jobs
db: # managed Postgres
redis: # external cache (x-eve.external: true)
サービス設計のルール
- サービスごとに責務を 1 つにします。 HTTP の処理とバックグラウンド処理を分けます。API サービスで定期ジョブも実行するべきではありません。
- Postgres にはマネージド DB を使用します。
x-eve.role: managed_dbを宣言し、プロビジョニング、接続、認証情報の注入はプラットフォームに任せます。手動で接続文字列を設定する必要はありません。 - 外部サービスを明示します。 Eve の外部でホストされるサービス(Redis、サードパーティ API)には、
x-eve.external: trueとx-eve.connection_urlを使用します。 - 単発タスクには
x-eve.role: jobを使用します。 マイグレーション、シード、データのバックフィルはジョブサービスであり、永続プロセスではありません。 - イングレスは意図を持って公開します。 外部からの HTTP アクセスが必要なサービスだけに
x-eve.ingress.public: trueを設定します。内部サービス間の通信には、クラスターのネットワークを使用します。 - ホスト名の戦略は早めに決めます。 すべての公開サービスには、デフォルトでプラットフォームが生成する URL が割り当てられます。よりわかりやすいプラットフォームのサブドメイン(
ingest.eve.example.com)を使うにはx-eve.ingress.aliasを追加するか、独自ドメインを使用するにはx-eve.ingress.domains: [limelee.com]を設定します。カスタムドメインは環境ごとに割り当てられ、最初にバインドした環境が優先されるため、宣言する前に、どの環境が apex ドメインを所有するか(通常はproduction)を設計してください。宣言方法と DNS 検証の流れについては、eve-manifest-authoringとeve-deploy-debuggingを参照してください。
固定アウトバウンド IP(エグレス)
多くのアプリでは、アウトバウンドの送信元 IP を気にする必要はありません。ただし、送信元 IP を許可リストに登録するベンダー(カメラ、決済処理業者、送信元 IP に厳しい制限を設けているパートナー API)と連携する場合は、サービスレベルで固定エグレスをオプトインするよう宣言します。プラットフォームは hostNetwork: true を使用するパブリックエグレスのノードグループに Pod をスケジュールし、共有 NAT マッピングではなく、安定した予測可能なアウトバウンド経路をサービスに提供します。これは必要な 1 つか 2 つのサービスに対する意図的なアーキテクチャ上の選択として扱い、デフォルトにはしないでください。
アプリのオブジェクトストレージ
ファイル(アップロード、アバター、エクスポート)を保存する必要があるアプリは、マニフェストでオブジェクトストアのバケットを宣言できます。
services:
api:
x-eve:
object_store:
buckets:
- name: uploads
visibility: private
- name: avatars
visibility: public
Note: アプリのオブジェクトストア用データベーススキーマは存在しますが、マニフェストからの自動プロビジョニングはまだ接続されていません。現在の状況については、
references/object-store-filesystem.mdを参照してください。
接続されると、プラットフォームは STORAGE_ENDPOINT、STORAGE_ACCESS_KEY、STORAGE_SECRET_KEY、STORAGE_BUCKET、STORAGE_FORCE_PATH_STYLE をサービスコンテナに注入します。
認証情報の分離:アプリの Pod が受け取るのは、プラットフォーム内部の認証情報ではなく、アプリにスコープされたストレージ認証情報です。侵害されたアプリのサービスが、プラットフォームのバケットや他のテナントのデータにアクセスすることはできません。この境界を念頭に設計してください。アプリ間で認証情報を共有しようとせず、各アプリのバケットを宣言して、プラットフォームからスコープされたキーを発行してもらってください。
Cloud FS/Google Drive ストレージ
ドキュメント指向のストレージには、Cloud FS マウントを使用してください。各組織が BYOA OAuth 認証情報を使用して独自の Google Drive を接続し、フォルダーを組織のファイルシステムにマウントします。
eve integrations configure google-drive --client-id "..." --client-secret "..."
eve integrations connect google-drive
eve cloud-fs mount --org org_xxx --provider google-drive --folder-id <id> --label "Shared Drive"
アプリは Eve の Cloud FS インターフェース(eve cloud-fs ls、eve cloud-fs search、およびマウントごとの Cloud FS API ルート)を通じて、マウントされた Drive のコンテンツを参照、検索できます。これはオブジェクトストアのバケットを補完するものです。共有ドキュメントや共同作業には Cloud FS を、アプリが管理するバイナリーアセットにはオブジェクトストアを使用してください。
プラットフォームから注入される変数
デプロイされたすべてのサービスは、EVE_API_URL、EVE_PUBLIC_API_URL、EVE_PROJECT_ID、EVE_ORG_ID、EVE_ENV_NAME を受け取ります。サーバー間の呼び出しには EVE_API_URL を使用してください。ブラウザー向けコードには EVE_PUBLIC_API_URL を使用してください。URL をハードコードせず、これらの変数を読み取るようにアプリを設計してください。
リファレンスアーキテクチャ:SPA + API + マネージド DB
最も一般的な Eve のフルスタックパターンです。nginx をフロントに配置した SPA が API 呼び出しを内部バックエンドにプロキシし、マネージド Postgres と eve-migrate でスキーマを管理します。
サービス構成
services:
web: # nginx SPA (public ingress, proxies /api/ → api service)
api: # NestJS/Express backend (internal, no public ingress)
db: # managed Postgres 16
migrate: # eve-migrate job (runs SQL migrations)
nginx プロキシを使う理由:Web サービスの nginx が /api/ を内部 API サービスにリバースプロキシします。これにより CORS が不要になり、API ホスト名をハードコードする必要がなくなり、SPA から同一オリジンでバックエンドにアクセスできます。API サービスにパブリックなイングレスはなく、クラスター内からのみアクセスできます。
マニフェストの構成
services:
api:
build:
context: ./apps/api
dockerfile: ./apps/api/Dockerfile
ports: [3000]
environment:
NODE_ENV: production
DATABASE_URL: ${managed.db.url}
CORS_ORIGIN: "https://myapp.eve.example.com"
# No x-eve.ingress — API is internal only
web:
build:
context: ./apps/web
dockerfile: ./apps/web/Dockerfile
ports: [80]
environment:
API_SERVICE_HOST: ${ENV_NAME}-api # k8s service DNS for nginx proxy
depends_on:
api:
condition: service_healthy
x-eve:
ingress:
public: true
port: 80
alias: myapp # https://myapp.{org}-{project}-{env}.eve.example.com
migrate:
image: public.ecr.aws/w7c4v0w3/eve-horizon/migrate:latest
environment:
DATABASE_URL: ${managed.db.url}
MIGRATIONS_DIR: /migrations
x-eve:
role: job
files:
- source: db/migrations
target: /migrations
db:
x-eve:
role: managed_db
managed:
class: db.p1
engine: postgres
engine_version: "16"
nginx プロキシ
Web サービスの Dockerfile は Vite で SPA をビルドし、nginx を介して配信します。nginx の設定では envsubst を使って、コンテナの起動時に ${API_SERVICE_HOST} を解決します。
server {
listen 80;
root /usr/share/nginx/html;
index index.html;
location /api/ {
proxy_pass http://${API_SERVICE_HOST}:3000/;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_buffering off;
}
location / {
try_files $uri $uri/ /index.html;
}
location /health {
return 200 "ok";
add_header Content-Type text/plain;
}
}
マニフェスト内の API_SERVICE_HOST: ${ENV_NAME}-api は k8s のサービス名(例:sandbox-api)に解決され、nginx に安定した内部 DNS の宛先を与えます。
スキーマ管理のための Eve-Migrate
Eve は、専用のマイグレーションランナー public.ecr.aws/w7c4v0w3/eve-horizon/migrate:latest を提供します。タイムスタンプのプレフィックスが付いたプレーンな SQL ファイルを使用し、schema_migrations テーブルで追跡します(冪等性があり、チェックサムを検証し、トランザクション処理を行います)。
db/
migrations/
20260312000000_initial_schema.sql
20260312100000_seed_data.sql
20260315000000_add_status_column.sql
x-eve.files を使って、マイグレーションをコンテナにマウントします。パイプラインの migrate ステップはデプロイ後に実行されます(先にマネージド DB をプロビジョニングする必要があります)。
TypeORM、Knex、Flyway のマイグレーションは使用しないでください。複雑さが増し、Eve プラットフォームのマイグレーション追跡方式と乖離するためです。eve-migrate ランナーを使うことで、ローカル開発とステージングの動作を一致させられます。
マルチステージ Dockerfile
API Dockerfile(NestJS/Node):
FROM node:22-slim AS base
WORKDIR /app
ENV PNPM_HOME="/pnpm" PATH="$PNPM_HOME:$PATH"
RUN corepack enable && corepack prepare pnpm@latest --activate
FROM base AS deps
COPY package.json pnpm-lock.yaml ./
RUN pnpm install --frozen-lockfile 2>/dev/null || pnpm install
FROM deps AS build
COPY tsconfig.json ./
COPY src ./src
RUN pnpm build
FROM node:22-slim AS production
WORKDIR /app
RUN groupadd --gid 1000 node || true && useradd --uid 1000 --gid node --shell /bin/bash --create-home node || true
COPY --from=deps /app/node_modules ./node_modules
COPY --from=build /app/dist ./dist
COPY package.json ./
USER node
ENV NODE_ENV=production PORT=3000
EXPOSE 3000
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
CMD node -e "fetch('http://localhost:3000/health').then(r => r.ok ? process.exit(0) : process.exit(1)).catch(() => process.exit(1))"
CMD ["node", "dist/main.js"]
Web Dockerfile(Vite SPA + nginx):
FROM node:22-slim AS build
WORKDIR /app
ENV PNPM_HOME="/pnpm" PATH="$PNPM_HOME:$PATH"
RUN corepack enable && corepack prepare pnpm@latest --activate
COPY package.json pnpm-lock.yaml ./
RUN pnpm install --frozen-lockfile 2>/dev/null || pnpm install
COPY tsconfig.json vite.config.ts index.html ./
COPY src ./src
RUN pnpm build
FROM nginx:alpine AS production
COPY --from=build /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/templates/default.conf.template
EXPOSE 80
HEALTHCHECK --interval=30s --timeout=5s --start-period=5s --retries=3 \
CMD wget --no-verbose --tries=1 --spider http://localhost/health || exit 1
CMD ["nginx", "-g", "daemon off;"]
慣例:node:22-slim をベースにし、corepack 経由で pnpm を使用し、ロックファイルを固定し、API は非 root ユーザーで実行します。両方のサービスにヘルスチェックを設定します。
データベース設計
プロビジョニング
マニフェストでマネージドデータベースを宣言します。
services:
db:
x-eve:
role: managed_db
managed:
class: db.p1
engine: postgres
engine_version: "16"
他のサービスでは、接続 URL を ${managed.db.url} として参照します。
スキーマ戦略
- マイグレーションはプレーンな SQL です。
db/migrations/にタイムスタンプを接頭辞にした SQL ファイルを作成します(例:20260312000000_initial.sql)。eve-migrate で実行します(上記のリファレンスアーキテクチャを参照)。本番環境のスキーマを手作業で変更しないでください。 - 最初から RLS を考慮して設計します。 マルチテナントデータを含むすべてのテーブルに
org_id TEXT NOT NULL、RLS ポリシー、およびセッションコンテキストを設定するDatabaseServiceを用意します(下記を参照)。後から行レベルセキュリティを導入するのは大変です。 - 変更前に調査します。
eve db schemaを使用して現在のスキーマを確認します。開発中のアドホックなクエリにはeve db sql --env <env>を使用します。 - アプリデータとエージェントデータを分けます。 個別のスキーマまたは命名規則を使用します。アプリのテーブルはプロダクトに、エージェントのテーブルはメモリと連携に使用します(ストレージパターンについては
eve-agent-memoryを参照)。
RLS + DatabaseService パターン(NestJS)
NestJS でマルチテナント RLS を実現する実証済みのパターンでは、リクエストスコープのトランザクションラッパーと raw pg.Pool(ORM ではありません)を使用します。
db.ts — 起動時のヘルスチェックを含むプール設定:
import { Pool } from 'pg';
const databaseUrl = process.env.DATABASE_URL || 'postgresql://app:app@localhost:5432/myapp';
const parsed = new URL(databaseUrl);
const isLocal = ['localhost', '127.0.0.1'].includes(parsed.hostname);
export const pool = new Pool({
connectionString: databaseUrl,
ssl: !isLocal ? { rejectUnauthorized: false } : undefined,
});
database.service.ts — RLS コンテキストを含むトランザクションラッパー:
import { Injectable } from '@nestjs/common';
import type { PoolClient, QueryResult, QueryResultRow } from 'pg';
import { pool } from '../db';
export interface DbContext {
org_id: string;
user_id?: string;
}
@Injectable()
export class DatabaseService {
async withClient<T>(context: DbContext | null, fn: (client: PoolClient) => Promise<T>): Promise<T> {
const client = await pool.connect();
try {
await client.query('BEGIN');
if (context?.org_id) {
await client.query("SELECT set_config('app.org_id', $1, true)", [context.org_id]);
}
if (context?.user_id) {
await client.query("SELECT set_config('app.user_id', $1, true)", [context.user_id]);
}
const result = await fn(client);
await client.query('COMMIT');
return result;
} catch (error) {
await client.query('ROLLBACK');
throw error;
} finally {
client.release();
}
}
async query<T extends QueryResultRow>(ctx: DbContext | null, sql: string, params?: unknown[]): Promise<QueryResult<T>> {
return this.withClient(ctx, (client) => client.query<T>(sql, params));
}
async queryOne<T extends QueryResultRow>(ctx: DbContext | null, sql: string, params?: unknown[]): Promise<T | null> {
const result = await this.query<T>(ctx, sql, params);
return result.rows[0] ?? null;
}
}
このパターンを使う理由
set_config('app.org_id', $1, true)はトランザクションスコープであり、接続がプールに返却されると自動的にクリアされます。- すべてのデータベースアクセスが
withClientを経由するため、クエリの実行前に必ず RLS コンテキストが設定されます。 - ORM のオーバーヘッドがなく、raw SQL によりクエリプランと結合を完全に制御できます。
DbContextオブジェクトはreq.user(Eve 認証ミドルウェアによって設定)から導出されます。
RLS ポリシーテンプレート(マイグレーション SQL でテーブルごとに適用):
ALTER TABLE my_table ENABLE ROW LEVEL SECURITY;
CREATE POLICY my_table_select ON my_table FOR SELECT
USING (current_setting('app.org_id', true) IS NOT NULL
AND org_id = current_setting('app.org_id', true));
CREATE POLICY my_table_insert ON my_table FOR INSERT
WITH CHECK (current_setting('app.org_id', true) IS NOT NULL
AND org_id = current_setting('app.org_id', true));
CREATE POLICY my_table_update ON my_table FOR UPDATE
USING (current_setting('app.org_id', true) IS NOT NULL
AND org_id = current_setting('app.org_id', true))
WITH CHECK (current_setting('app.org_id', true) IS NOT NULL
AND org_id = current_setting('app.org_id', true));
テーブルの規約:すべてのテーブルに id UUID PRIMARY KEY DEFAULT gen_random_uuid()、org_id TEXT NOT NULL、created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() を設けます。変更可能なテーブルには、トリガーを使用した updated_at TIMESTAMPTZ も設けます。最初のマイグレーションで pgcrypto 拡張機能を有効にします。
アクセスパターン
| クエリの実行者 | 方法 | 認証 |
|---|---|---|
| アプリサービス | サービス環境変数内の ${managed.db.url} | デプロイ時に接続文字列を注入 |
| CLI 経由のエージェント | eve db sql --env <env> | ジョブトークンでアクセスを制限 |
| RLS 経由のエージェント | app.current_user_id() を使用した SQL | ランタイムがセッションコンテキストを設定 |
ビルドとリリースのパイプライン
標準的なフロー
本番アプリはすべて、build → release → deploy → migrate → smoke-test の順に実行してください。
pipelines:
deploy:
steps:
- name: build
action:
type: build # Creates BuildSpec + BuildRun, produces image digests
- name: release
depends_on: [build]
action:
type: release # Creates immutable release from build artifacts
- name: deploy
depends_on: [release]
action:
type: deploy # Deploys release to target environment
- name: migrate
depends_on: [deploy]
action:
type: job
service: migrate # Runs eve-migrate against the managed DB
- name: smoke-test
depends_on: [migrate]
script:
run: ./scripts/smoke-test.sh
timeout: 300
この順序が重要な理由:
buildは SHA256 イメージダイジェストを生成します。releaseはその正確なダイジェストを固定します。deployは固定されたリリースを使用します。ビルドしたものとまったく同じものをデプロイできるため、タグのずれや「latest」にまつわる予期せぬ問題がありません。- 管理対象 DB は最初にプロビジョニングする必要があるため、
migrateはdeployの後に実行します。eve-migrate ジョブが保留中の SQL マイグレーションを適用します。 smoke-testは、パイプラインが成功を報告する前に、デプロイ済みサービスをエンドツーエンドで検証します。
レジストリの選択
| オプション | 使用する場面 |
|---|---|
registry: "eve" | デフォルトです。JWT 認証を備えた内部レジストリです。最も簡単に設定できます。 |
| BYO レジストリ(GHCR、ECR) | Eve の外部からイメージにアクセスする必要がある場合、または既存の CI がある場合。 |
registry: "none" | 公開ベースイメージのみを使用する場合。カスタムビルドはありません。 |
GHCR の場合は、リポジトリを自動的にリンクできるよう、Dockerfile に OCI ラベルを追加します。
LABEL org.opencontainers.image.source="https://github.com/YOUR_ORG/YOUR_REPO"
ビルド設定
カスタムイメージを使用するすべてのサービスに build セクションが必要です。
services:
api:
build:
context: ./apps/api
dockerfile: Dockerfile
image: ghcr.io/org/my-api
マルチステージ Dockerfile を使用します。BuildKit はマルチステージにネイティブで対応しています。OCI ラベルは最終ステージに配置します。
デプロイと環境
環境戦略
| 環境 | 種類 | 用途 | パイプライン |
|---|---|---|---|
staging | 永続 | 統合テスト、デモ | deploy |
production | 永続 | ライブトラフィック | deploy(昇格あり) |
preview-* | 一時 | PR プレビュー、機能ブランチ | deploy(自動クリーンアップ) |
マニフェストで各環境をパイプラインにリンクします。
environments:
staging:
pipeline: deploy
production:
pipeline: deploy
デプロイパターン
標準デプロイ:eve env deploy staging --ref main --repo-dir . を実行すると、リンクされたパイプラインがトリガーされます。
直接デプロイ(パイプラインを経由しない):緊急時やシンプルな構成では、eve env deploy staging --ref <sha> --direct を使用します。
プロモーション:ステージングで一度ビルドし、同じリリース成果物を本番環境にプロモートします。ビルドステップのダイジェストが引き継がれるため、同一のイメージが保証されます。
復旧
デプロイに失敗した場合:
- 診断:
eve env diagnose <project> <env>— ヘルス状態、最近のデプロイ、サービスのステータスを表示します。 - ログ:
eve env logs <project> <env>— コンテナーの出力を表示します。 - ロールバック:直近の正常なリリースを再デプロイします。
- リセット:
eve env reset <project> <env>— 最終手段として、最初から再プロビジョニングします。
アプリはロールバックに対応できるよう設計してください。マイグレーションは前方互換性を持たせ、サービスはローリングデプロイ中のスキーマバージョンの不一致を適切に処理する必要があります。
システムアプリパターン
プラットフォーム層と管理者向けアプリは、顧客向けアプリと同じ Eve パイプラインでデプロイされ、特別な経路はありません。Eve Dashboard がその代表例です。標準のビルド → リリース → デプロイのフローで提供され、他のアプリとともに CI の公開マトリックスに含まれ、顧客向けアプリと同じ SSO + アナリティクス API を利用します。社内の管理コンソール、自社プラットフォームのコントロールプレーン、またはシステム層の画面を構築する場合は、このパターンを採用してください。メリットは一貫性です。すべてのアプリで、デプロイ、認証、オブザーバビリティの仕組みが統一されます。独自の「プラットフォーム専用」デプロイ経路を設けるのは避けてください。そうした経路は放置され、他の経路との乖離が進みます。
アプリ統合の組織ごとの OAuth
Google Drive、Slack、その他の OAuth プロバイダーと連携するアプリでは、組織ごとの認証情報を使用します(BYOA -- Bring Your Own App)。各組織が独自の OAuth アプリを登録することで、ブランディング、スコープ、レート制限、認証情報のローテーションを管理できます。
eve integrations configure google-drive --client-id "..." --client-secret "..."
eve integrations connect google-drive
設計上の留意点:Google Drive のデータや Slack のメッセージを利用するアプリでは、OAuth 認証情報をアプリ内に保存せず、Eve API 経由で統合トークンを参照してください。プラットフォームは、組織が登録した OAuth アプリの認証情報を使ってトークンの更新を処理します。
ワークフローのイベントトリガー
ワークフローはプラットフォームイベントによってトリガーでき、イベントに応じた自動化が可能です。
workflows:
on-deploy:
trigger:
system.event: environment.deployed
steps:
- name: smoke-test
script:
run: ./scripts/smoke-test.sh
on-ingest:
trigger:
system.event: doc.ingest.completed
steps:
- name: process
agent: doc-processor
イベントソースには、GitHub Webhook、Slack イベント、システムイベント(デプロイ、ビルド、取り込み)、cron スケジュール、手動トリガーが含まれます。トリガーの構文については eve-pipelines-workflows を、イベントの全カタログについては references/events.md を参照してください。
アプリ CLI フレームワーク — Eve の流儀
API を持つすべてのアプリには CLI を用意してください。 これが Eve の流儀です。エージェントは生の REST 呼び出しではなく、CLI コマンドを通じてアプリのデータを操作します。CLI を使うと、エージェントはアプリのドメインに対して、発見しやすく、認証を意識せずに使え、型安全なアクセスができます。1 回の操作に必要な LLM 呼び出しを 3~5 回(curl)から 1 回(CLI コマンド)に減らし、URL の組み立てや JSON の引用符付けをなくし、HTTP ステータスコードではなくドメイン固有のエラーメッセージを提示できます。
CLI ファーストが重要な理由
コーディングエージェントがアプリのデータを読み書きする際、適切な URL、認証ヘッダー、JSON 本文を含む curl コマンドを組み立てるか、eden projects list --json を実行するかを選べます。CLI のアプローチは、あらゆる観点で優れています。
| 観点 | CLI | 生の REST |
|---|---|---|
| 認証 | 自動で処理(EVE_JOB_TOKEN を自動的に読み込み) | ヘッダーを手動で組み立て |
| URL | 不要(CLI がサービス URL を認識) | EVE_APP_API_URL_* から組み立て |
| 発見しやすさ | myapp --help | OpenAPI 仕様またはドキュメントを読む |
| エラー | ドメイン固有のメッセージ | HTTP ステータスコード |
| LLM コスト | 操作ごとに 1 回の呼び出し | 操作ごとに 3~5 回の呼び出し |
マニフェストで CLI を宣言する
services:
api:
x-eve:
api_spec:
type: openapi
cli:
name: myapp # Binary name on $PATH
bin: cli/bin/myapp # Pre-bundled executable (repo-bundled mode)
プラットフォームはマニフェスト内の x-eve.cli を使ってサービスを自動検出し、プロジェクト内のすべてのエージェントジョブで $PATH から利用できるようにします。明示的な with_apis は必要ありません。マニフェストで CLI を宣言するだけで、すべてのエージェントが利用できます。エージェントは myapp --help を実行して機能を確認します。宣言の詳細については eve-manifest-authoring を、実装パターン(バンドル、環境変数の契約、テスト)の全体については references/app-cli.md を参照してください。
設計ガイダンス
- CLI は早い段階で構築します。 API が「完成」するまで待たないでください。最初の API エンドポイントと並行して CLI の構築を始めましょう。エージェントはすぐに CLI を使い始めます。
- API の全機能を反映します。 すべての REST エンドポイントに CLI サブコマンドを用意します。
GET /items→myapp items list、POST /items→myapp items create --file data.jsonのようにします。 - どこでも
--jsonをサポートします。 デフォルトの出力は人が読みやすいテーブル形式にし、--jsonではエージェントのパイプラインで使える機械可読な出力を返します。 - 単一ファイルとしてバンドルします。 esbuild を使って、リポジトリにコミットする自己完結型の Node.js スクリプトを生成します。起動時の遅延はゼロです。
- エージェントのスキルから CLI を使うよう指定します。 スキルの指示では「
myapp items listを使ってください」と記載し、「API に curl してください」とは書かないでください。
埋め込み会話
アプリにチャット機能(サイドバーペイン、インラインアシスタント、「このオブジェクトについて質問」ボタンなど)がある場合は、Eve の埋め込み会話 API とチャット SDK を使用してください。認証、ディスパッチ、SSE、再接続、楽観的送信を独自に実装しないでください。プラットフォームは、アプリから渡される識別子(例:myapp:{project_id}:{conversation_id})をキーとしたスレッドの検索または作成ルーティング、JWKS で検証されるブラウザートークン、再生可能な SSE ストリームを提供します。これらは @eve-horizon/chat と @eve-horizon/chat-react にまとめられており、UI コードは約 50 行で済みます。
設計上の考慮事項:
- 安定したキーを使って、製品オブジェクト(ドキュメント、プロジェクト、チケットなど)を Eve のスレッドに対応付けます。スレッドの状態はプラットフォームが管理し、アプリは
thread_idを参照する製品側のプロジェクションを管理します。 - 外部ゲートウェイと同じプリミティブを使い、
chat.yaml、エージェント、チーム、またはワークフローを経由してルーティングします。チャットも他のディスパッチ先と同じです。 - フェーズ 1 のプッシュには、スナップショットとポーリングによる追いつき処理を組み合わせた SSE を使用します。永続的なソケットを前提とせず、一時的な再接続に対応できるよう UI を設計してください。
SDK の構成とルーティングパターンについては、eve-read-eve-docs スキル内の references/eve-sdk.md を参照してください。
アプリのデプロイ解除/削除のライフサイクル
環境とプロジェクトのライフサイクル全体を管理します。
# Undeploy services (stops pods, keeps env record and history)
eve env undeploy <project> <env>
# Delete environment entirely (cascades to managed DB, secrets)
eve env delete <project> <env>
# Delete project (cascades to all environments, artifacts, history)
eve project delete <project-id>
アプリはきれいに解体できるよう設計してください。マイグレーションは冪等にし、管理対象の DB の削除は取り消せないことに注意してください。また、環境を削除した後もパイプラインの履歴は監査ログに保持されます。
シークレットと構成
スコープモデル
シークレットは、プロジェクト > ユーザー > 組織 > システムの優先順位で段階的に解決されます。プロジェクトレベルの API_KEY は、組織レベルの API_KEY を上書きします。
設計ルール
- シークレットはプロジェクトごとに設定します。
eve secrets set KEY "value" --project proj_xxxを使います。プロジェクトのシークレットは、それぞれのプロジェクト内で完結させてください。 - マニフェストで補間を使用します。 サービスの環境変数ブロックでは
${secret.KEY}を参照してください。プラットフォームがデプロイ時に解決します。 - デプロイ前に検証します。
eve manifest validate --validate-secretsを実行し、シークレット参照の不足によってデプロイが失敗する前に検出してください。 - ローカル開発には
.eve/dev-secrets.yamlを使用します。 本番環境のシークレットキーに対応するローカル値を設定してください。このファイルは gitignore の対象です。 - 環境変数にシークレットを直接保存しないでください。 必ず
${secret.KEY}による補間を使用してください。これにより、シークレットはプラットフォームの解決処理と監査の仕組みを通じて扱われます。
サービストークン(マニフェストで宣言、デフォルトは読み取り専用)
Eve API を自身のために呼び出すサービスには、EVE_SERVICE_TOKEN が自動的に注入されます。権限はサービスごとにマニフェストで明示的に宣言し、デフォルトは読み取り専用です。書き込みが必要なサービスは、明示的にオプトインする必要があります。これは最小権限の契約として扱ってください。サービスをデプロイしただけで、ひそかに書き込みアクセス権が付与されるべきではありません。各サービスに必要な最小限の機能を宣言し、コードレビューで差分を確認できるようにしてください。宣言構文については、eve-read-eve-docs の references/secrets-auth.md と references/manifest.md を参照してください。
スコープ付きジョブトークン(プラットフォーム層の最小権限)
マルチテナントリソースにまたがるジョブをオーケストレーションするプラットフォーム層のアプリでは、権限名だけでは不十分です。orgfs:read だけでは、どのプレフィックスが対象かは分かりません。トークンレベルで最小権限を適用するには、ワークフローのステップにリソーススコープを宣言してください。
workflows:
scoped-review:
scope:
orgfs: { allow_prefixes: [/groups/projects/proj-a/**] }
steps:
- name: review
agent: { name: reviewer }
scope:
cloud_fs: { allow_mount_ids: [mount_a] }
スコープの軸はアクセスバインディングと一致します。orgfs、orgdocs、envdb、cloud_fs です。ワークフロー、ステップ、呼び出しのスコープは積集合が適用され(積集合が空の場合はデフォルトで拒否されます)、jobs.token_scope として永続化されます。オーケストレーターは同じスコープを使ってワークスペースの .org マウントを構成し、ジョブトークンを発行します。これにより、ディスク上の表示範囲と API の権限が一致します。各ステップが実際に必要とするリソースだけを参照するよう、プラットフォーム層のワークフローを設計してください。
Git 認証情報
エージェントにはリポジトリへのアクセスが必要です。プロジェクトシークレットとして github_token(HTTPS)または ssh_key(SSH)を設定してください。Git 操作時に、ワーカーがこれらを自動的に注入します。
SSO 認証
アプリへの SSO の追加
Eve は、定型コードをなくす共有認証パッケージを提供しています。約 25 行のコードで Eve SSO ログインを追加できます。
バックエンド(@eve-horizon/auth):
import { eveUserAuth, eveAuthGuard, eveAuthConfig } from '@eve-horizon/auth';
app.use(eveUserAuth()); // Parse tokens (non-blocking)
app.get('/auth/config', eveAuthConfig()); // Serve SSO discovery
app.get('/auth/me', eveAuthGuard(), (req, res) => {
res.json(req.eveUser); // { id, email, orgId, role }
});
app.use('/api', eveAuthGuard()); // Protect all API routes
フロントエンド(@eve-horizon/auth-react):
import { EveAuthProvider, EveLoginGate } from '@eve-horizon/auth-react';
function App() {
return (
<EveAuthProvider apiUrl="/api">
<EveLoginGate>
<ProtectedApp />
</EveLoginGate>
</EveAuthProvider>
);
}
コンポーネントから認証済み API 呼び出しを行うには、createEveClient を使用します。
import { createEveClient } from '@eve-horizon/auth-react';
const client = createEveClient('/api');
const res = await client.fetch('/data');
カスタム認証ゲート — 読み込み状態やログイン状態を制御する必要がある場合(カスタムログインページや、より充実した読み込み UI など)は、EveLoginGate の代わりに useEveAuth() を直接使用します。
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 <AppShell user={user} onLogout={logout}><Routes /></AppShell>;
}
export default function App() {
return (
<EveAuthProvider apiUrl={API_BASE}>
<AuthGate />
</EveAuthProvider>
);
}
仕組み
EveAuthProviderがsessionStorageにキャッシュ済みトークンがあるか確認します- トークンがない場合、SSO ブローカーの
/sessionを確認します(ルートドメインの Cookie) - SSO セッションがある場合、新しい Eve RS256 トークンを取得します
- セッションがない場合、ログインフォームを表示します(SSO リダイレクトまたはトークン貼り付け)
- すべての API リクエストに
Authorization: Bearer <token>が含まれます
NestJS バックエンド
main.ts で eveUserAuth() をグローバルミドルウェアとして適用します。既存のコントローラーが req.eveUser ではなく req.user を想定している場合は、Eve のロールをアプリ固有のロールにマッピングする薄いブリッジを、1 か所に追加してください。
import { eveUserAuth } from '@eve-horizon/auth';
app.use(eveUserAuth());
app.use((req, _res, next) => {
if (req.eveUser) {
req.user = { ...req.eveUser, role: req.eveUser.role === 'member' ? 'viewer' : 'admin' };
}
next();
});
自動注入される変数
プラットフォームは、デプロイ済みコンテナーに EVE_SSO_URL、EVE_API_URL、EVE_ORG_ID、EVE_PROJECT_ID を注入します。手動で設定する必要はありません。フロントエンドからアクセス可能な SSO URL には、マニフェストの env ブロックで ${SSO_URL} を使用します。プラットフォームがブランディング、リダイレクト許可リスト、アプリスコープの認証ポリシーをプロジェクト単位で適用できるのは、EVE_PROJECT_ID によるものです。
パスワードレス/マジックリンクアプリ
アプリのオンボーディングが「管理者がユーザーを招待し、ユーザーがメールをクリックしてアプリにアクセスする」という流れなら、パスワードレスアプリとしてモデル化してください。不要なパスワードリセットフローを構築する必要はありません。マニフェストで宣言すれば、プラットフォームがブランドに合わせたマジックリンクのログインページを表示します。
x-eve:
auth:
login_method: magic_link # or password_or_magic_link, password
self_signup: false # unknown emails return generic success, no email
invite_requires_password: false # invite acceptance skips /set-password
設計上の留意点:
- ユーザーは自己登録ではなく招待によって作成されます。それに応じて管理者向けの招待 UX を設計してください。
- マジックリンクのメールでは、招待メールと同じ
x-eve.brandingブロックが再利用されます。ブランディングの情報源は 1 つで、メールの用途は 2 種類です。 - Eve API はメール送信前に、既存ユーザー、保留中の招待、ドメイン登録を確認して利用資格を判定します。そのため、アプリ側で対応しなくてもアカウント列挙攻撃への防御が維持されます。
アプリのブランディング(招待メールとマジックリンクメール)
アプリごとのブランディングはコードではなく、マニフェストで設定します。一度宣言すると、プラットフォームが招待メール、マジックリンクメール、SSO ログインページに適用します:
x-eve:
branding:
app_name: "ACME Portal"
app_logo_url: "https://app.example.com/assets/logo.svg"
primary_color: "#1f6feb"
email_from_name: "ACME Portal"
reply_to_email: "[email protected]"
support_email: "[email protected]"
support_url: "https://example.com/help"
From: の表示名とメール本文にアプリのアイデンティティが反映されます。送信者アドレスはプラットフォームのデフォルトのままです(テナントごとに検証済みの From: を使用するのは後のフェーズです)。ブランディングはプロジェクトのプロパティとして扱い、組織ごとの設定にはしないでください。
アプリの組織アクセスとアプリ内管理者招待
デフォルトでは、アプリのスコープはプロジェクト所有者の組織です。複数の顧客組織にサービスを提供するマルチテナントアプリでは、許可リストを宣言し、(必要に応じて)アプリ内管理者招待を有効にします:
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 admins
設計上の留意点:
- バックエンドでは
eveUserAuth()ではなくeveAppUserAuth()を使用し、認可がプロジェクト所有者の組織ではなくアプリのポリシーに基づくようにします。このミドルウェアはGET /auth/app-accessを参照し、X-Eve-Org-Id、?eve_org_id=、または最初に許可された組織から組織を選択します。 - 管理者ページでは、汎用的な組織招待 API ではなく、ブランディングされたマジックリンクでオンボーディングを送信する
POST /auth/app-invitesを使用します。プラットフォームは送信前に、組織のロールとアプリの許可リストポリシーを適用します。 - React SDK は、組織/管理者組織の一覧表示とメンバーの招待に
useEveAppAccess()を提供します。
ドメインベースのサインアップ(パス C 自動紐付け)
アプリで顧客のメールドメイン全体にサービスを提供する必要がある場合(「@yourcompany.com のアドレスを持つ人は誰でも、メンバーとして org_yourcompany に入る」など)は、ドメインサインアップを使用します。Eve は最初のマジックリンク要求時に一度限りの招待を作成し、ユーザーが承認するとメンバーシップを自動的に紐付けます。1 つのプロジェクトで異なるドメインを異なる組織に振り分けられます:
x-eve:
auth:
org_access:
mode: allowlist
allowed_orgs: [org_acme, org_globex]
domain_signup:
enabled: true
domains:
- { domain: acme.com, target_org: org_acme, role: member }
- { domain: globex.com, target_org: org_globex }
設計上の留意点:
- ルールは宣言順に評価され、最初に一致したルールが適用されます。より具体的なパターンを先に宣言してください。
- 信頼モデルは「運用者が宣言し、プラットフォームが信頼する」です。v1 では DNS の証明は行いません。
free-mail.exampleを宣言すると整合性に関する警告が出ますが、宣言は許可されます。 - 明示的な保留中の招待(パス B)は、常にドメインサインアップ(パス C)より優先されます。許可された組織のメンバーシップを持つ既存ユーザー(パス A)には、標準のブランディングされた送信が行われます。
- ルールを削除すると新規サインアップは停止しますが、既存のメンバーシップは遡って削除されません。
eve org members removeを明示的に実行するオフボーディングを計画してください。 auth.domain_signup.invite_createdおよびauth.domain_signup.member_attachedイベントで監査できます。
プロジェクトスコープのリダイレクト許可リスト(カスタムドメイン)
アプリがクラスターのドメイン配下ではなく、独自のカスタムドメインで動作する場合は、SSO がリダイレクトできるオリジンを宣言します。これを設定しないと、SSO はリダイレクト先をプラットフォームのデフォルトのホスト名に書き換えるため、ブランディングされたアプリの URL が失われます:
x-eve:
auth:
allowed_redirect_origins:
- https://app.example.com
- https://www.example.com
最終的な許可リストは、マニフェストのエントリー、プロジェクト独自の対象となるカスタムドメイン、および(mode: allowlist の場合)allowed_orgs に含まれるプロジェクトが所有する組織横断のカスタムドメインの和集合です。これは、従来のハードコードされた EVE_DEFAULT_DOMAIN 許可リストに代わるものです。また、プラットフォームは SSO セッションクッキーに SameSite=None; Secure が設定されることを保証します。そのため、カスタムドメインのフロントエンドからのクロスサイト /session プローブにも認証情報が含まれ、Cookie 属性を自分で設定する必要はありません。
設計ルール
- 独自の認証ではなく SDK を使用します。 SDK により、手作業で実装した約 750 行の認証コードを約 50 行に置き換えられます。
- まずはブロッキングしないミドルウェアを使用します。 グローバルに
eveUserAuth()を使用し、保護するルートにはeveAuthGuard()を適用します。これにより、公開ルートと非公開ルートを混在させられます。 /auth/configエンドポイントがハンドシェイクになります。 フロントエンドはバックエンドのeveAuthConfig()エンドポイントを呼び出して SSO URL を取得します。これにより、フロントエンドがプラットフォームの環境変数に依存しなくなり、ローカル開発環境とデプロイ済み環境のどちらでも同じように動作します。- トークンの古さを考慮して設計します。
orgsJWT クレームはトークン発行時のメンバーシップを反映します(TTL は 1 日です)。必要に応じて、即時失効にはstrategy: 'remote'を使用します。
SDK の詳細なリファレンスについては、eve-read-eve-docs スキル内の references/auth-sdk.md を参照してください。
オブザーバビリティとデバッグ
デバッグの手順
次の段階に沿って調査を進めます。
1. Status → eve env show <project> <env>
2. Diagnose → eve env diagnose <project> <env>
3. Logs → eve env logs <project> <env>
4. Pipeline → eve pipeline logs <pipeline> <run-id> --follow
5. Recover → eve env deploy (rollback) or eve env reset
最初は上の段階から始めます。段階が進むほど詳細が増え、コストも高くなります。ほとんどの問題は段階 1~2 で解決します。
パイプラインの可観測性
パイプラインの実行をリアルタイムで監視します。
eve pipeline logs <pipeline> <run-id> --follow # stream all steps
eve pipeline logs <pipeline> <run-id> --follow --step build # stream one step
失敗したステップには失敗のヒントが含まれ、該当する場合はビルド診断へのリンクが表示されます。
ビルドのデバッグ
ビルドに失敗した場合:
eve build list --project <project_id>
eve build diagnose <build_id>
eve build logs <build_id>
一般的な原因:レジストリの認証情報が不足している、Dockerfile のパスが一致しない、ビルドコンテキストが大きすぎる。
ヘルスチェック
ヘルスエンドポイントを備えたサービスを設計します。Eve はヘルス状態をポーリングして、デプロイの準備状況を判断します。ready === true かつ active_pipeline_run === null になった時点で、デプロイは完了です。
設計チェックリスト
サービス構成:
- 各サービスの責務が1つに定められている
- Postgres が必要な場合、マネージド DB が宣言されている
- 外部サービスに
x-eve.external: trueが設定されている - イングレスが有効なのは、外部公開するサービスのみである
- ハードコードされた URL ではなく、プラットフォームが注入する環境変数を使用している
- ホスト名の方針が選択されている(生成 URL、エイリアス、または
x-eve.ingress.domainsによるカスタムドメイン) - 送信元 IP の許可リストを使用するベンダーと連携するサービスに限り、安定したエグレスを有効にしている
データベース:
- マイグレーションは
db/migrations/内のタイムスタンプ接頭辞付きプレーン SQL ファイルである - マニフェストで
x-eve.filesマウントを指定したeve-migrateジョブサービスが宣言されている -
DatabaseServiceが RLS コンテキスト(set_config)を使用して、すべての DB アクセスをラップしている -
org_idを持つすべてのテーブルに RLS ポリシーが設定されている -
pgcrypto拡張機能が有効で、UUID 主キーとupdated_atトリガーが使用されている - アプリデータとエージェントデータが、スキーマまたは規約によって分離されている
パイプライン:
- 標準の
build → release → deploy → migrate → smoke-testパイプラインが定義されている - マイグレーションのステップがデプロイ後に実行される(先にマネージド DB が存在している必要があります)
- スモークテストスクリプトがデプロイ済みのサービスをエンドツーエンドで検証する
- レジストリが選択され、認証情報がシークレットとして設定されている
- Dockerfile に OCI ラベルが設定されている(GHCR 用)
- イメージダイジェストがリリースを通じて引き継がれる(タグベースのデプロイは行わない)
環境:
- ステージング環境と本番環境が定義されている
- 各環境がパイプラインにリンクされている
- 昇格ワークフローが定義されている(一度ビルドし、複数回デプロイ)
- 復旧手順が把握されている(診断 -> ロールバック -> リセット)
シークレット:
-
eve secrets setを使って、すべてのシークレットがプロジェクトごとに設定されている - マニフェストで
${secret.KEY}補間を使用している -
eve manifest validate --validate-secretsが成功する - ローカル開発用の
.eve/dev-secrets.yamlが存在する - Git 認証情報(
github_tokenまたはssh_key)が設定されている - サービストークンの権限がサービスごとに宣言されている(デフォルトは読み取り専用。書き込み権限は明示的に有効化)
認証:
- バックエンドに
@eve-horizon/authミドルウェアが追加されている(eveUserAuth+eveAuthGuard、または複数組織対応アプリではeveAppUserAuth) - 認証設定エンドポイントが SSO ディスカバリー(
eveAuthConfig)を提供する - フロントエンドが
@eve-horizon/auth-reactでラップされている(EveAuthProvider+EveLoginGate、またはカスタムのuseEveAuthゲート) - フロントエンドからの認証済み API 呼び出しに
createEveClientを使用している - プラットフォームが注入する認証環境変数(
EVE_SSO_URL、EVE_ORG_ID、EVE_PROJECT_ID)を使用している - Eve のロールが、コントローラーに分散させず、1か所(ブリッジミドルウェア)でアプリのロールにマッピングされている
- パスワードレス認証の場合:
x-eve.auth.login_method: magic_link+invite_requires_password: false - マルチテナントの場合:
allowed_orgsを宣言したx-eve.auth.org_access.mode: allowlistを設定し、アプリ内の管理者による招待ではPOST /auth/app-invitesを使用する - ドメインベースのオンボーディングの場合:
domain_signup.domainsでルールごとのtarget_orgを宣言し、より具体的なルールを先に記載する - カスタムドメインの場合:
x-eve.auth.allowed_redirect_originsを宣言し、eve project auth-contextで解決済みの許可リストを確認する - ブランド設定を行う場合:
x-eve.brandingを宣言する(招待メールとマジックリンクメールで共有)。From:の表示名とロゴを設定する
アプリ CLI(Eve の方式):
- アプリ API がドメイン CLI(例:
eden projects list)でラップされている -
nameとbinを指定したx-eve.cliにより、CLI がマニフェストで宣言されている - CLI が単一ファイルの実行可能ファイルとしてバンドルされている(Node.js では esbuild を使用)
- CLI が
EVE_APP_API_URL_{SERVICE}とEVE_JOB_TOKENを自動的に読み取る - すべての CLI コマンドが、機械可読な出力を得るための
--jsonに対応している - エージェントスキルが生の curl/REST 呼び出しではなく、CLI コマンドを参照している
可観測性:
- サービスがヘルスエンドポイントを公開している
- デバッグの手順(status -> diagnose -> logs -> recover)を把握している
-
eve pipeline logs --followでパイプラインのログにアクセスできる
相互参照
- マニフェストの構文とオプション:
eve-manifest-authoring - デプロイコマンドとエラーの解決:
eve-deploy-debugging - シークレット管理とアクセスグループ:
eve-auth-and-secrets - パイプラインとワークフローの定義:
eve-pipelines-workflows - ローカル開発ワークフロー:
eve-local-dev-loop - この基盤へのエージェント機能の追加:
eve-agentic-app-design - Auth SDK と SSO の統合:
eve-read-eve-docs→references/auth-sdk.md - オブジェクトストレージとファイルシステム:
eve-read-eve-docs→references/object-store-filesystem.md - 外部連携(Slack、GitHub):
eve-read-eve-docs→references/integrations.md