Imported from syoch/infrastructure (
AGENTS.md). Install upstream withnpx skills add syoch/infrastructure. Copyright stays with the author.
AGENTS.md - syoch/infrastructure
プロジェクト概要
Android Device Provisioning Portal (以下 portal) のリポジトリ。 Obtainium と連携し、APK の配信・更新管理を行う。
ディレクトリ構成
| ディレクトリ | 役割 |
|---|---|
backend/ |
Python/FastAPI アプリ本体 (core のみ) |
backend/core/ |
コア (extension registry/loader, backup manager, database, auth, config) |
extensions/<feature>/portal_<feature>/ |
第一者拡張機能 (obtainium, control_plane, app_portal, storage)。portal.extensions entry point で登録。テストは extensions/<feature>/tests/ |
device_agent/ |
汎用デバイスエージェント (ポータル標準コンポーネント) |
frontend/ |
フロントエンド (Svelte 5 + Vite SPA)。E2E spec は frontend/src/features/<feature>/e2e/ |
tests/ |
共有テストフィクスチャ (config.test.json, bootstrap/, uploads/) |
extensions/obtainium/tests/avd/ |
Obtainium 統合試験 (AVD 使用) |
nixos/ |
NixOS モジュール (portal-service.nix, portal-device-agent.nix) |
contrib/ |
非ポータル資産 (gamemcbe, tailscale, Android root ツール) と専用 flake |
docs/ |
ドキュメント (外部拡張の追加方法は docs/external-extensions.md) |
examples/ |
外部拡張のサンプル (hello_extension) |
.opencode/control-plane/ |
Control plane 設計ドキュメント (Phase 1-12) |
開発環境
# nix develop で全ツールが利用可能になる
# pwd はリポジトリルート必須 (flake.nix を検出するため)
nix develop --command bash
# または direnv が自動で有効化 (.envrc: "use flake")
dev shell で提供される主なツール: Node.js, Python 3 (sqlalchemy, fastapi, uvicorn), mypy, Chromium, curl, jq, rsync, android-tools (adb)。
Android root 系ツール (aapt, scrcpy, dtc, usbutils, sunxi-tools, ksud-next) は contrib/ の flake で提供 (nix develop ./contrib)。
フロントエンドの開発 (dev サーバ → API)
SvelteKit の dev サーバ (Vite) は同一オリジンの /api 等をバックエンドへプロキシします。接続先は PORTAL_API_BASE で指定できます。
# ローカル backend (既定: http://localhost:8000)
cd frontend && npm run dev
# 本番/リモートのポータルに対して dev する
PORTAL_API_BASE=https://portal.syoch.org npm run dev
# もしくは frontend/.env に設定 (frontend/.env.example 参照)
# PORTAL_API_BASE=... 接続先 API のベース URL
# PORTAL_API_INSECURE=1 自己署名 TLS を許容
# PORTAL_API_ACCESS_CLIENT_ID / PORTAL_API_ACCESS_CLIENT_SECRET
# Cloudflare Access のサービス トークンを
# プロキシに付与 (保護された本番へ届かせる)
- 認証はアプリ側の
Bearer <tk_...>(localStorage)で行われるため、dev → 本番でもトークンを入れれば API を叩けます。 - 本番が Cloudflare Access 配下の場合は上記サービス トークン(または tailnet/VPN 経由のホスト名)を使ってください。
テスト実行
# 全テスト
make test
# バックエンドテストのみ (機能別に extensions/<feature>/tests/ に配置)
make test-backend
# 注: control_plane_ws テストは `nix develop` 環境 (websockets パッケージ) を必要とします
# Playwright E2E テスト (pwd はリポジトリルート必須)
make test-e2e
# または
nix develop --command bash -c "cd frontend && npx playwright test --reporter=list"
# Obtainium 統合試験 (AVD 起動中 + バックアップ tarball 必須)
make test-obtainium BACKUP=path/to/backup.tgz
# Obtainium スモークテスト (3アプリ)
make test-obtainium-smoke BACKUP=path/to/backup.tgz
テスト作成の注意
網羅性
- 正常系だけでなく異常系・境界値もカバーする
- Pydantic バリデーション:
overrideSource: null,preferredApkIndex: nullが通ること - 削除操作時は dialog accept のハンドリングを明示的に行う
sleep を避ける
sleepによる待機は絶対に使わない- 状態検出:
ui_dump(uiautomator), ファイル存在チェック, HTTP ステータス確認 - UI 要素の表示待機: Playwright の
waitForSelector,expect().toBeVisible()
テスト環境
- E2E:
nix develop --commandで実行 (pwd はリポジトリルート必須) - Obtainium: AVD 起動中 + バックアップ tarball 必須
- シードデータ:
tests/bootstrap/seed_backup.tar.gz - テスト自動起動: Playwright が
webServer設定で事前にサーバーを起動
クリーンアップ
- 各テストは作成したデータを自身で削除する
- テスト間の状態共有は避ける (独立性)
UI テスト固有の注意
- ハッシュベースルーティング (
#/dashboard,#/edit?type=app&id=...) - confirm ダイアログ:
page.once('dialog', ...)で事前登録 - APK アップロード: マジックバイト
PK\x03\x04で始まる必要あり - テスト終了後、作成したアプリ・カテゴリは必ず削除する
ポートとプロセス
- Portal サーバー:
http://localhost:8000(テスト時) - テスト DB:
tests/portal_test.db(SQLite, WAL モード) - テスト設定:
tests/config.test.json - Control plane REST:
/api/control/{devices,acls,operations,commands,events} - Control plane WS:
/api/control/devices/{device_id}/ws?token=tk_xxx - Control plane bridge:
portal-control-bridge --server-url <...> --bootstrap-token <...> - Device agent:
portal-device-agent --config /path/to/config.json(汎用 shell-command ベース) - Device dogfooding:
extensions/control_plane/portal_control_plane/bridge.pyがacl.*/device_admin.*を advertise - WebUI:
#/controlルート (#/control/devices,#/control/acl,#/operations) - Control 画面:
frontend/src/features/control_plane/ - Operations クエリ:
#/operations?status=&from=&to=&op=&limit=&offset= - 管理者昇格 CLI:
python3 backend/manage.py --config <cfg> control set-admin --device-id <id> - Schema renderer:
frontend/src/entries/schema_api.ts+frontend/src/lib/schema.svelte.ts+frontend/src/components/Schema{Form,Editor}.svelte(JSON Schema → form,ui_hint.widget: json|textarea|password) - 拡張機能の選択: config の
extensionsに ID を列挙 (storage/obtainium/control-plane/app-portal) - NixOS モジュール:
nixosModules.portal/nixosModules.portal-device-agent(services.portal/services.portal-device-agent) - 設計:
.opencode/control-plane/PHASE{1..12}.mdを参照
注意事項
nix developはリポジトリルートで実行してください (flake.nix の検索)- 非ポータル資産は
contrib/(専用 flake) に分離されています - プレコミットフックが秘密情報のスキャンを行います (
.githooks/pre-commit) - Cloudflare Access が
portal.syoch.orgを保護しています - APK は Content-Addressable Storage (SHA-256) で管理されます