PHPackages                             blackops/skeleton - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. blackops/skeleton

ActiveProject

blackops/skeleton
=================

Feature-first BlackOps application skeleton.

1.2.0(3w ago)024↓66.7%MITPHPPHP &gt;=8.5

Since Jul 13Pushed 2w agoCompare

[ Source](https://github.com/kubotak-is/blackops-skeleton)[ Packagist](https://packagist.org/packages/blackops/skeleton)[ RSS](/packages/blackops-skeleton/feed)WikiDiscussions main Synced 1w ago

READMEChangelogDependencies (6)Versions (4)Used By (0)

BlackOps Quickstart
===================

[](#blackops-quickstart)

Feature-firstのBlackOps Application Skeletonである。Inline `GET /welcome`、調査用Inline Failure `POST /failures`、Database Transaction付き`POST /orders`、Deferred `POST /reports`、PostgreSQL 18、FrankenPHP 1、PHP 8.5 CLIを含む。

Distribution Status
-------------------

[](#distribution-status)

このDirectoryはFramework Repository `main`のPreview Quickstartであると同時に、Packagist Package `blackops/skeleton`のSource of Truthである。Release WorkflowがこのDirectoryだけを`kubotak-is/blackops-skeleton`へSplitし、Frameworkと同じVersionで公開する。公開済みStable `1.1.0`にはHeader AuthenticationとPhase 13のDatabase／Transaction Journeyが未収録で、`POST /orders`も含まれない。

Local検証ではCommitted QuickstartだけをPackage Rootへ抽出し、SkeletonとFrameworkを`symlink=false`の別々のLocal Repositoryとして通常／`--no-scripts` Create-projectする。Remote検証は空のComposer HomeからPackagist Packageだけを取得する。

Setup
-----

[](#setup)

```
php bin/setup
docker compose build app http
docker compose run --rm app composer install
pnpm install --frozen-lockfile
docker compose run --rm app php blackops operation:list
docker compose run --rm app php blackops database:status
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 database:seed
docker compose run --rm app php blackops frontend:generate
docker compose run --rm app php blackops frontend:check
pnpm test
docker compose up -d
```

Composer `create-project`は`post-create-project-cmd`から同じ`bin/setup`を実行する。`--no-scripts`で作成した場合、またはSetupを明示的に再実行する場合はProject Root内外のどのWorking Directoryからでも`php /path/to/my-app/bin/setup`を実行できる。Setupは`.env`がない場合だけ`.env.example`をCopyし、`var/build/`と`var/log/`を準備する。既存`.env`は変更しない。

Quickstartの`bootstrap/app.php`はFrameworkの`withEnvironmentFile()`でProcess Environmentを優先してOptional `.env`を一度だけSnapshotします。Classic／Workerの`public/index.php`／`public/worker.php`は`BlackOps\Http\SapiRuntime`へApplicationを渡すだけです。Environment、PSR-7、SAPI Emit、UUIDv7のRuntime実装はFrameworkが所有するため、Skeleton Composer MetadataへそのRuntime Packageを重複宣言しません。DBAL／MigrationsなどApplicationが実ImportするPackageだけをApplication Direct Dependencyとして追加します。

```
composer create-project blackops/skeleton my-app 1.1.0
```

```
composer create-project --no-scripts blackops/skeleton my-app 1.1.0
php my-app/bin/setup
```

Framework Repository内のQuickstartへ直接`composer install`するだけでは、未公開の`blackops/framework:^1.2` candidateをPackagistから解決できず、`main` PreviewのSourceと一致しない。認証付き`/welcome`／`/reports`とPhase 13の`/orders`を試す場合は、[利用者向けQuickstartのRepository main Preview手順](../../docs/guide/mvp-sample.md#repository-main-preview)でFramework SourceをLocal Path Repositoryとして組み合わせ、version `1.2.0`を明示する。準備後のPreview Directoryで`php bin/setup`を実行する。

Install、Build、MigrationはImage startupに含まれない。Default `docker compose up` はHealthyなPostgreSQLとWorker Mode HTTPだけを起動し、Deferred Worker、Scheduler、Migration、Retention Purgeは起動しない。HTTP Portは `.env` の `HTTP_PORT` で変更でき、既定は8080である。

Setupは次手順を表示するだけで、Composer／pnpm Install、Network Access、Docker、Database、Migration、Artifact Build、Frontend生成、TypeScript Test、Worker、Scheduler、Retentionを実行しない。Backendだけで利用する場合はpnpm、`frontend:generate`、`frontend:check`、`pnpm test`を省略できる。`build:compile`はBackend Artifactと小さいFrontend Contract Artifactを作るが、TypeScript SourceやNode Dependencyを生成しない。

Frontend Operation Objects
--------------------------

[](#frontend-operation-objects)

Install直後のSkeletonは、Application所有の`config/frontend.php`、`package.json`、`pnpm-lock.yaml`、`tsconfig*.json`、`resources/js/application/`、`tests/Frontend/`を含む。`resources/js/blackops/`はPHP Operationから明示生成するため配布物へ固定せず、`.gitignore`対象にする。

```
pnpm install --frozen-lockfile
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
pnpm test
```

`frontend:generate`と`frontend:check`は`build:compile`を暗黙実行しない。`frontend:check`はFreshならExit 0、Missing／DriftならExit 1、Config／Artifact／Contract不正ならExit 2を返す。生成したModuleはCallableでもThenableでもなく、通信する`.fetch()`、一回だけ状態を取得する`.status()`、明示した期限まで待つ`.wait()`、送信しない`.toRequest()`、URLを返す`.url()`、Readonly Metadataを持つOperation Objectである。

```
import {
  GenerateReport,
  ShowWelcome,
  operationOptions,
} from './resources/js/application/operations';

const options = operationOptions(runtimeToken, 'http://127.0.0.1:8080');

// Input: {}
// URL output: "/welcome"
const welcomeUrl = ShowWelcome.url();

// Input: reportName、write-onlyのrecipientEmail、呼出単位のCredential
// Request output: POST /reports、JSON Body、Content-Type、X-Sample-Token
const request = GenerateReport.toRequest(
  { reportName: 'weekly', recipientEmail },
  options,
);

// Inputは同じ。OutputはHTTP 202を表すaccepted Result。
const result = await GenerateReport.fetch(
  { reportName: 'weekly', recipientEmail },
  options,
);

if (result.ok && result.kind === 'accepted') {
  result.status; // 202
  result.data.operationId;

  const current = await GenerateReport.status(result.data.operationId, options);

  const controller = new AbortController();
  const terminal = await GenerateReport.wait(result.data.operationId, {
    ...options,
    signal: controller.signal,
    maxWaitMilliseconds: 15_000,
  });

  if (terminal.ok && terminal.kind === 'completed') {
    terminal.data.outcome.reportName;
    terminal.data.outcome.location;
  }
}

ShowWelcome.type;     // 'welcome.show'
ShowWelcome.method;   // 'GET'
ShowWelcome.path;     // '/welcome'
ShowWelcome.strategy; // 'inline'
```

`recipientEmail`の名前と型は送信に必要なWrite-only Input Contractへ含むが、その実値をGenerated Tree、Result、Log Helperへ埋め込まない。`X-Sample-Token`もApplicationが呼出単位で注入する。Generated TypeはAuthentication／Authorization、CORS、CSRF、Encryption、Browser Storageを代替しない。

`.fetch()`は202後に自動Pollingしない。`.status()`は`GET /operations/{operationId}`を一回だけ呼び、`.wait()`だけがServerの`Retry-After`に従って有限に待つ。`.wait()`には購読可能なAbort Signalと正の`maxWaitMilliseconds`が必須である。401、404、410、500、Network Error、不正Responseでは自動Retryせず停止する。

Quickstartは`SampleOperationStatusAuthorizer`を`ApplicationServiceProvider`から明示Bindingする。同じ`user` Actorが受け付けたOperationだけを同じActorが参照できる最小Same-origin Exampleであり、Tenant／Role／Resourceを扱うProduction Policyではない。ProductionではApplicationが認証方式とStatus参照Policyを実装する。Operation IDは相関KeyでありSecretではないが、IDを知っているだけでは参照できない。

HTTP
----

[](#http)

```
curl -H 'X-Sample-Token: local-example' http://127.0.0.1:8080/welcome

curl -X POST -H 'Content-Type: application/json' \
  -H 'X-Sample-Token: local-example' \
  -d '{"reference":"order-001"}' \
  http://127.0.0.1:8080/orders

curl -X POST -H 'Content-Type: application/json' \
  -H 'X-Sample-Token: local-example' \
  -d '{"reportName":"weekly","recipientEmail":"reports@example.com"}' \
  http://127.0.0.1:8080/reports

curl -i -H 'X-Sample-Token: local-example' \
  http://127.0.0.1:8080/operations/

curl -X POST -H 'Content-Type: application/json' \
  -H 'X-Sample-Token: local-example' \
  -d '{"reference":"incident-demo-001","sensitiveNote":"private diagnostic note"}' \
  http://127.0.0.1:8080/failures
```

Failure Responseの`operationId`はHuman／JSONのどちらでも調べられる。

```
docker compose run --rm app php blackops operation:inspect
docker compose run --rm app php blackops operation:inspect  --json
```

`config/logging.php`はApplication／Framework相関Logを`var/log/application.jsonl`へJSONLで出力する。Docker-only QuickstartではPostgreSQLをHostへPublishせず、ViewerもCLI ContainerのLoopback限定なので、Hostから`php blackops operation:viewer`を実行したりHost Browserで開いたりできない。Docker利用時は上記Human／JSON Inspectを使う。ViewerはConsumer E2Eと同様にViewer／HTTP Clientを同じCLI ContainerとLocal Network Namespaceへ置く場合だけ検証でき、Host Browser公開の手順ではない。

BrowserでViewerを利用するには、Application／PHP CLI／PostgreSQL／Browserが同じLocal Network Namespaceから到達可能なNative Runtimeを準備し、Project Rootで`php blackops operation:viewer`を明示実行する。Non-loopback Bindへ緩めずLoopbackを維持し、起動ごとに変わるBootstrap Tokenを保存／共有しない。

OrderはDefault DBAL `Connection`をConstructor InjectionしたRepositoryを使う。`CreateOrder::handle()`の`#[Transactional]`が最外Transactionを所有し、Container管理の`CreateOrderCommand`は`#[Transactional]`で同じConnectionへNested Required参加する。Order Rowと成功Terminal JournalがCommitした後、`#[AfterCommit]`の`RecordOrderCommit::record()`がCommit確認Rowを追加する。

```
{"reference":"order-001","status":"created"}
```

After Commitは同期Best-effortであり、Process Crashを越える再送やDeliveryを保証しない。Email、Webhook、Message Publish等をat-least-onceで配送する場合はTransactional Outboxを使い、Relay停止／再開、Retry、Dead Letter再開を明示的に運用する。外部配送のExactly Onceは保証しない。

`X-Sample-Token`はAuthentication Middlewareだけが読み、Operation Value、Transport、Journalへ保存しない。`SAMPLE_API_TOKEN`が未設定または空なら、Authenticatorは既知値へFallbackせず構成を失敗させる。

Reportの`recipientEmail`は業務上のSensitive値の例である。HTTP内で完了するValidation RejectionのObserved Projectionでは`var/log/journal.jsonl`へ`[masked]`として記録する。Valid Deferred ReportのWorker EventはJSONLへ転送せず、Raw ValueとActor IDを含むCanonical PostgreSQL Journalが正本となる。既定Observer Deliveryは`best_effort`である。

このHeader Token認証はLocal Development専用のExampleである。ProductionではApplicationがSession、Bearer Token、External IdP等の認証方式とSecret管理へ置き換える。

Headerを省略するとAuthentication MiddlewareはAnonymousとして通過させ、`#[Authorize]`を持つOperationがOperation ID付き401でRejectする。不正なHeader値はOperation受付前の401となり、Operation IDとJournalを作らない。

```
curl -i http://127.0.0.1:8080/welcome
curl -i -H 'X-Sample-Token: invalid' http://127.0.0.1:8080/welcome
```

### FrankenPHP Worker Mode

[](#frankenphp-worker-mode)

Default HTTPはWorker Modeであり、Process単位でApplication、Environment、Configuration、Compile済みRuntimeを一度だけ構成する。

```
docker compose up -d http
curl -H 'X-Sample-Token: local-example' http://127.0.0.1:8080/welcome
```

`FRANKENPHP_MAX_REQUESTS`はWorker Threadを安全に再起動するRequest上限で、既定は1000である。Frameworkは各Request前にDatabase Connectionをhealth-checkし、Stale Connectionをcloseして一度再接続する。再接続できない場合は500として失敗し、成功Responseとして扱わない。Request終了時はOperation Scopeを検査し、JSONL Observerをflushする。Throwableまたは未完了TransactionのConnectionは次Requestへ持ち越さずcloseする。

FrankenPHPはWorker callback終了後にRequest Superglobalをcleanupするが、`$_ENV`はRequest間でresetしない。EntrypointはProcess開始時の`$_ENV`へ毎Request復元する。Application ServiceはRequest Body、Actor、Tenant、PSR-7 Request等のRequest固有Stateをpropertyやstaticへ保持せず、Operation ValueまたはExecution Contextから受け取る。

Classic Modeは明示Fallbackとして`classic-mode` Profileから起動できる。既定Portは8081で、`CLASSIC_HTTP_PORT`から変更する。

```
docker compose --profile classic-mode up -d http-classic
curl -H 'X-Sample-Token: local-example' http://127.0.0.1:8081/welcome
```

Worker and Maintenance
----------------------

[](#worker-and-maintenance)

```
docker compose run --rm app php blackops worker:run --iterations=1
docker compose --profile worker up worker
docker compose run --rm app php blackops retention:plan
docker compose run --rm app php blackops retention:purge --dry-run
docker compose --profile maintenance up scheduler
```

Sample Report Operationは最初のAttemptでRetryを要求し、次のAttemptで成功する。変更を適用するPurgeは `--confirm` を明示した場合だけ実行する。

Removing Starter Features
-------------------------

[](#removing-starter-features)

Welcomeは `app/Feature/Welcome/`、Reportは `app/Feature/Report/`、Local Failure Exampleは`app/Feature/Diagnostics/`を削除するだけでよい。Orderを削除する場合は`app/Feature/Order/`と`migrations/Version20260718000000.php`を削除し、`ApplicationServiceProvider`からOrder用の3 Bindingを外す。`config/operations.php` のDiscovery Rootへ追加したOperationは次のBuildで検出される。

Operation自身に `handle(OperationValue): Outcome`、またはAttempt等の実行情報が必要なら `handle(OperationValue, ExecutionContext): Outcome` を定義するTyped Self-handledが標準形である。ValueとOutcomeはSignatureから推論され、`#[Accepts]`、`#[Returns]`、`OperationResult::completed()`は不要である。予期された業務拒否はFrameworkの `OperationRejectedException`、一時障害は通常のRetryable Exceptionをthrowする。FrameworkはOperationをContainerへAutowireする。Repository InterfaceやExternal Client等のConstructor Dependencyが必要な場合だけ、`ServiceProvider` を作り `config/app.php` の `services` へ登録する。

Creating an Operation
---------------------

[](#creating-an-operation)

Framework所有のGeneratorから、Build可能なTyped Self-handled Operation、Value、Outcomeを作成できる。

```
php blackops make:operation Billing/CreateInvoice --type=billing.invoice.create
php blackops build:compile
```

Generatorは`app/Feature/Billing/CreateInvoice/`へ3 Fileを作成する。既存Fileは上書きせず、Route、Deferred設定、Build、Database操作は行わない。生成後のSourceはApplication所有であり、Framework Updateによって書き換えられない。CommandとStubはFramework Packageが所有するため、`composer update blackops/framework`後の新規生成には更新済み実装が使われ、Projectの`blackops`を置き換える必要はない。

Creating a Migration
--------------------

[](#creating-a-migration)

Application固有のMigrationはFramework所有Generatorで作成する。

```
php blackops make:migration CreateOrdersTable
```

最初の実行時に`migrations/`が作られ、UTC Versionの`App\Migrations` Classが生成される。`up()`／`down()`へApplicationのSQLを記述した後、Framework Migrationと同じ明示Commandで確認・適用する。

```
php blackops database:status
php blackops database:migrate --dry-run
php blackops database:migrate
```

Generator自身はDatabase接続、Migration適用、Buildを行わない。Application Migrationがない場合、空の`migrations/`は不要である。

Creating and Running a Seeder
-----------------------------

[](#creating-and-running-a-seeder)

Install直後のSkeletonは標準Root `app/Infrastructure/Seed/DatabaseSeeder.php`を持つ。追加SeederはFramework所有Generatorで作り、Rootから明示順に実行する。

```
php blackops make:seeder Catalog/ProductSeeder
php blackops database:migrate
php blackops build:compile
php blackops database:seed
```

Migration、Build、Seedは互いを暗黙実行しない。SeederはBuild時に検出されるため、Source変更後は必ず再Buildする。Transaction、再実行方針、Conflict処理はApplicationが所有する。

###  Health Score

43

—

FairBetter than 89% of packages

Maintenance96

Actively maintained with recent releases

Popularity8

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity53

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~14 days

Total

3

Last Release

21d ago

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/13742370?v=4)[KenjiroKubota](/maintainers/kubotak-is)[@kubotak-is](https://github.com/kubotak-is)

---

Top Contributors

[![kubotak-is](https://avatars.githubusercontent.com/u/13742370?v=4)](https://github.com/kubotak-is "kubotak-is (45 commits)")

### Embed Badge

![Health badge](/badges/blackops-skeleton/health.svg)

```
[![Health](https://phpackages.com/badges/blackops-skeleton/health.svg)](https://phpackages.com/packages/blackops-skeleton)
```

###  Alternatives

[getgrav/grav

Modern, Crazy Fast, Ridiculously Easy and Amazingly Powerful Flat-File CMS

15.7k89.2k1](/packages/getgrav-grav)[tempest/framework

The PHP framework that gets out of your way.

2.3k42.4k21](/packages/tempest-framework)[jaxon-php/jaxon-core

Jaxon is an open source PHP library for easily creating Ajax web applications

74153.1k35](/packages/jaxon-php-jaxon-core)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
