本文へ移動
BlackOps1.xは試験的なバージョンです。Production Readyは2.xを予定しています。Releases
BlackOps
Esc
navigateopen⌘Jpreview
このページの内容

BlackOps Board Reference Application

Reference Applicationを起動し、Authentication、Inline、Deferred、Browser outcomeまで完走する。

BlackOps Boardは、公開済みExperimental Stable 1.2.1のFramework機能を実際のBrowser JourneyへまとめたRepository Exampleです。Application-owned Authentication、SvelteKit Same-origin BFF、PostgreSQL、Inline Post/Comment、Deferred Weekly Digestを一つの構成で確認できます。Framework/Skeleton Packageには含まれず、外部Hostingもしていません。

BlackOps BoardのCredential-free Landing画面

画像はLocal Runtimeの登録前画面から生成しています。User、Password、Session、Local Absolute Pathを含みません。

Quickstartとの使い分け

入口 適した目的 含む範囲
Quickstart and Skeleton Frameworkの最短Contractを確認したい Typed Operation、Inline/Deferred HTTP、Worker、Journal、Status/Outcome、Generated Operation Object
BlackOps Board Application全体の責任分界をBrowserから追いたい Application-owned Identity、Framework Session Core、SvelteKit BFF、Inline CRUD、Deferred Progress、Accessible UI、Browser User Journey

最初のOperationを自分で書く場合はFirst Operationへ進んでください。BlackOps BoardはCore API一覧の代わりではなく、完成したApplicationで各Contractがどう接続されるかを説明するExample Guideです。

空のLocal Stateから起動する

Application Directoryへ移動し、次の順序を変えずに実行します。

cd examples/community-board
php bin/setup
docker compose build app http frontend
docker compose run --rm --no-deps app composer install --no-interaction --prefer-dist --no-progress
mise exec -- pnpm --dir frontend install --frozen-lockfile
docker compose up -d postgres
docker compose run --rm app php blackops database:migrate
docker compose run --rm app php blackops build:compile
docker compose run --rm app php blackops frontend:generate
docker compose run --rm app php blackops frontend:check
docker compose run --rm app php blackops database:seed
mise exec -- pnpm --dir frontend run check
mise exec -- pnpm --dir frontend run test
mise exec -- pnpm --dir frontend run build
docker compose --profile worker up -d postgres http frontend worker

bin/setup.env.exampleの空のBLACKOPS_STORAGE_KEY placeholderをstrict base64の32 random bytesへ置き換え、.envをmode 600で作成し、Runtime Directoryだけを準備します。Keyは出力しません。Dependency Install、Migration、Build、Generate、Seed、Startを暗黙に実行しません。database:seedはRoot DatabaseSeederからCommunity Board Seederを実行し、固定した3 User、3 Post、4 Commentを作ります。同じDatabaseで再実行しても重複しません。

既存.envがある場合、bin/setupはbyte/metadataを変更せず、Keyの追加・Rotation・暗黙の書換えも行いません。既存環境を移行するときは.envをBackupし、ApplicationのSecret-handling手順で32 byteを表すstrict base64の非空BLACKOPS_STORAGE_KEYを一つだけ追加または置換し、mode 600とAssignment数を確認します。Key Valueや.env内容を出力しないでください。Fresh setupが途中で失敗した場合は不完全な.envを残しません。

App\Security\SampleStorageKeyProviderはLocal/Test専用のApplication-owned Providerです。ProductionではKMS/Secret Managerなど承認済みのApplication-owned ProviderへBindingを置き換え、.env生成KeyをProductionへ持ち込まないでください。FrameworkはProduction Keyの生成、保存、Rotation、KMS接続を所有しません。

http://localhost:5173/loginを開き、次を入力します。

Email: ada@blackops.local
Password: BlackOpsBoardDemo!2026

このCredentialは公開Local/Test Fixtureであり、Production Secretではありません。非Local環境へExampleを移す前に変更または削除してください。SeedはSessionを作らず、Loginが通常のAuthentication Routeを通ってSessionを作ります。

User Journeyを確認する

  1. /postsでAda、Grace、Linusの3 Postを確認します。
  2. /posts/019b1000-0001-7000-8000-000000000101でAdaのPostとGrace/LinusのCommentを確認します。
  3. /posts/newで空のFormを送信し、Labelと関連したValidation Errorを確認してからPostを作ります。
  4. 作成したPostへCommentを追加し、OwnerだけにEdit/Delete Actionが見えることを確認します。
  5. /digests2026-W30を入力します。Form ActionはDeferred Operationを一度だけ送信し、202のOperation IDをProgress Routeへ渡します。
  6. Workerが処理すると、Progress UIはaccepted/runningからcompletedへ進み、Typed OutcomeのDigest Detailへ移動します。
  7. .envDIGEST_FAIL_FIRST_ATTEMPT=trueをLocal/Testで明示すると、Attempt 1後のretry_scheduledとAttempt 2のcompletedも確認できます。通常設定はfalseです。

DigestはUTC ISO Week内に存在するPost/Comment件数だけを集計したImmutable Snapshotです。同じUser/Weekを再生成すると別のDigest Rowを作ります。Postを後からHard Deleteしても既存Snapshotを書き換えず、次回生成時は現存Rowだけを数えます。

Architectureを追う

Browser
  -> SvelteKit same-origin UI / BFF
     -> Page Server Load / Form Action / Wait Endpoint
        -> Application-owned *.server.ts wrapper
           -> Server-only Generated Operation Object
              -> BlackOps PHP HTTP
                 -> Domain Service -> DBAL Repository -> PostgreSQL
                 -> Deferred Transport -> Worker -> Typed Outcome

SvelteKit server
  -> Generated Register / Login / Logout Operation Object
     -> BlackOps Ephemeral HTTP Lifecycle
        -> Identity Domain + Framework Session Core -> PostgreSQL

BrowserはSvelteKit Originだけへ接続します。Generated Moduleはfrontend/src/lib/server/blackops/generated/へ出力し、Application-owned .server.ts WrapperだけがImportします。Page Server LoadとForm Actionは画面に必要なSafe View Modelへ縮約し、BrowserへBackend URLやRaw Errorを透過しません。

app/Domain/Board/はPostの存在、Owner判定、Row Lockを伴う更新/削除、Post/Comment生成、Digest集計等の業務規則を所有します。app/Domain/Identity/はUser、Password、Email正規化、Registration Policyを所有し、BlackOps/Doctrine/Symfonyへ依存しません。app/Infrastructure/はDoctrine DBAL SQL、Clock、UUID、Session Identity接続、Seed、Development Retry Adapter等の技術詳細を所有します。app/Feature/のOperationはValueとActorをDomain Serviceへ渡し、Domain ResultをOutcomeへ変換するCoordinationに留まります。

Mutation Operationの#[Transactional]がApplication ConnectionのTransaction境界を作ります。Domain ServiceはBlackOps Attributeへ依存せず、Transactionを開始しません。Database/TransactionのFramework ContractはTransaction、Deferred StateはOutcomeを参照してください。

InlineとDeferredの境界を読む

Post Feed/Detail/Create/Edit/DeleteとComment CreateはInline Operationです。HTTP Request内でValidation、Authorization、Domain Mutation、Outcomeまで終わります。Malformed/Unknown/Non-owner Resourceは、存在を推測できない同じSafe Resultへ縮約します。

Weekly DigestはDeferred Operationです。.fetch()は202を返すだけで自動Pollingしません。SvelteKit ServerがCurrent Sessionを付けた.status()またはAbort/Deadline必須の.wait()を呼びます。BrowserはSame-origin Wait Endpointだけへ接続し、BlackOps Status Resourceへ直接接続しません。Lifecycleの一般ContractはInline and Deferredで確認できます。

AuthenticationとSensitive Dataを分離する

Register/Login/LogoutはAttributeなしでInlineへ解決されるTransactional/Ephemeral Operationです。Generated Operation Objectから通常のBlackOps HTTP Runtimeへ送り、Application-owned Identity DomainとFramework Session Coreへ接続します。PasswordはArgon2id Hash、Session TokenはSHA-256 HashだけをPostgreSQLへ保存します。Raw TokenはRegister/Login時にSvelteKit Serverへ一度だけ返し、HttpOnlySameSite=Strict、Path /のCookieへ入れます。

PasswordとRaw Session Tokenは#[Sensitive]なEphemeral Value/Outcomeにだけ存在します。FrameworkはReceived Valueを空Projection、Completed Outcomeを空OutcomeとしてJournalへ記録し、Outcome Store、Status API、Generated Artifact、Page Data、Browser Bundle、LogへCredentialを残しません。通常の認証済みOperationへ渡すのはActorRefだけで、PHP側のOwner PolicyとStatus Authorizerが最終判断を行います。Generated TypeやSvelteKitでのButton非表示は認証・認可を代替しません。一般的な責務表はSecurityを参照してください。

Local HTTPでは.env.exampleSESSION_COOKIE_SECURE=falseを明示します。HTTPSを使う非Local環境ではtrueを必須にし、TLS設定の回避目的でCookieを弱めないでください。

Applicationの確認を選ぶ

Application DirectoryでClean Installを実行すると、依存物とDatabase Volumeがない状態からLogin/Seed表示とCleanupまでを一度に検証できます。

docker compose --profile worker up -d postgres http frontend worker
php blackops database:status

Foundation、Identity、Post/Comment、Product Journey、Digest、BrowserのApplication Testは問題領域を分離します。Browser Testは実ChromiumでRegister、Logout、Login、Validation、Post、Comment、Edit、Digest Retry/Completion、Logoutを完走し、Keyboard、320px Layout、Light/Dark、Reduced Motion、axe、Credential非露出も検証します。Testingの組み立て方はTestingを参照してください。

Troubleshooting

Worker未起動

症状: Digest Progressがacceptedのまま進みません。

確認方法: docker compose --profile worker psworkerを確認し、docker compose --profile worker logs workerでClaimの有無を確認します。

修正方法: docker compose --profile worker up -d workerを実行します。PostgreSQLとPHP HTTP Runtimeも同時に起動しておきます。

Seed Conflict

症状: php blackops database:seedが固定の安全なMessageで非0終了します。

確認方法: 固定Seed IDまたは@blackops.local EmailのRowが、Source Fixtureと異なる表示名、時刻、本文、関連、Password Hashへ手動変更されていないか確認します。

修正方法: Seed外Dataを保持したまま該当Seed Rowを元へ戻すか、完全なLocal Resetならdocker compose down --volumesを実行します。SeedはConflict Rowを自動更新、削除、truncateしません。

Port衝突

症状: Composeが517380818082のBindに失敗します。

確認方法: docker compose psとHost側のPort利用状況を確認します。

修正方法: .envFRONTEND_PORTBLACKOPS_DEBUG_PORTBLACKOPS_CLASSIC_DEBUG_PORTを空きPortへ変更します。FRONTEND_ORIGINもFrontend Portへ合わせてから再起動します。

Generated Drift

症状: frontend:checkがMissing/Driftを返すか、SvelteKitのImport/Type Checkが失敗します。

確認方法: php blackops build:compileの後にphp blackops frontend:checkを実行します。

修正方法: php blackops frontend:generateで再生成し、frontend:checkとFrontend Buildをやり直します。Generated Directoryを手編集しません。

症状: Local Login後もCookieが送信されず、/loginへ戻ります。

確認方法: URLがHTTPかHTTPSか、.envSESSION_COOKIE_SECUREFRONTEND_ORIGINが一致するか確認します。

修正方法: 文書化したLocal HTTPだけでSESSION_COOKIE_SECURE=falseを使います。非Local HTTPSではtrueへ戻し、TLS終端とOriginを修正します。

停止と完全Cleanupには次を使います。

docker compose --profile worker --profile classic-mode down --volumes --remove-orphans

Community BoardはApplication-owned Reference Applicationとして、利用者がProject RootからApplicationのBrowser/API Testを実行し、公開Hostを前提にしない自己管理環境でJourneyを再現できることを確認します。Frameworkの内部運用記録を利用者向けの根拠として扱いません。Documentation WebsiteはCloudflare Pagesへ公開しています。