Restore API startup by isolating Bird activation (#334)
Keep Bird activation outside the PHP-FPM/nginx startup path so Bird configuration failures cannot make the core API unavailable. Retain guarded explicit activation and regression coverage.
This commit is contained in:
@@ -2,8 +2,5 @@
|
|||||||
set -e
|
set -e
|
||||||
|
|
||||||
mkdir -p /run/nginx
|
mkdir -p /run/nginx
|
||||||
if [ "${CONFIG_DB_TARGET:-live}" = "live" ]; then
|
|
||||||
php /var/www/html/scripts/bird-control-plane-auto-activate.php
|
|
||||||
fi
|
|
||||||
php-fpm -D
|
php-fpm -D
|
||||||
exec nginx -g "daemon off;"
|
exec nginx -g "daemon off;"
|
||||||
|
|||||||
@@ -44,15 +44,14 @@ deletion, physical gate actions, arbitrary recipients, attachments, or a
|
|||||||
generic proxy. Outbound references are durably reserved before a provider
|
generic proxy. Outbound references are durably reserved before a provider
|
||||||
request; an ambiguous outcome must be inspected through
|
request; an ambiguous outcome must be inspected through
|
||||||
`/bird/control-plane/v1/messages/by-reference` and is never blindly retried.
|
`/bird/control-plane/v1/messages/by-reference` and is never blindly retried.
|
||||||
Credentials and write switches can only be changed by the guarded CLI startup
|
Credentials and write switches can only be changed by the guarded CLI
|
||||||
path or its explicit break-glass command, never by a web request.
|
activation command, never by a web request.
|
||||||
|
|
||||||
### Schema deployment and preflight
|
### Schema deployment and preflight
|
||||||
|
|
||||||
The durable webhook and outbound-action ledgers use checked-in schema version
|
The durable webhook and outbound-action ledgers use checked-in schema version
|
||||||
`1`. Schema changes are never run from a web request or worker. Production
|
`1`. Schema changes are never run from a web request, worker, or core API
|
||||||
startup applies and verifies the schema automatically; these commands remain
|
startup. Use these commands for deployment preflight and recovery:
|
||||||
available for manual preflight and break-glass recovery:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
php scripts/bird-control-plane-schema.php check
|
php scripts/bird-control-plane-schema.php check
|
||||||
@@ -64,15 +63,21 @@ php scripts/bird-control-plane-schema.php check
|
|||||||
publishes read-only `schema: {ready, version, expectedVersion, missing}` state.
|
publishes read-only `schema: {ready, version, expectedVersion, missing}` state.
|
||||||
Ledger-dependent reads, webhook ingestion, messages, and operational actions
|
Ledger-dependent reads, webhook ingestion, messages, and operational actions
|
||||||
fail closed with HTTP `503` and code `bird_schema_not_ready` until the preflight
|
fail closed with HTTP `503` and code `bird_schema_not_ready` until the preflight
|
||||||
is ready. Production auto-activation applies and verifies the schema before
|
is ready. Explicit activation applies and verifies the schema before enabling
|
||||||
enabling any Bird write switch; the manual command remains a diagnostic and
|
any Bird write switch.
|
||||||
break-glass path.
|
|
||||||
|
|
||||||
### Fail-closed production auto-activation
|
### Fail-closed production activation
|
||||||
|
|
||||||
The Coolify production container runs activation before PHP-FPM or nginx. It
|
Bird activation is deliberately separate from PHP-FPM and nginx startup, so a
|
||||||
first transactionally disables bootstrap readiness and all three write
|
Bird provider or configuration failure cannot make the core API unavailable.
|
||||||
switches. It then canonicalizes the existing legacy workspace/channel
|
Run the guarded activation explicitly in the deployed API container:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
php /var/www/html/scripts/bird-control-plane-auto-activate.php
|
||||||
|
```
|
||||||
|
|
||||||
|
The command first transactionally disables bootstrap readiness and all three
|
||||||
|
write switches. It then canonicalizes the existing legacy workspace/channel
|
||||||
configuration, applies and checks schema version `1`, validates configured
|
configuration, applies and checks schema version `1`, validates configured
|
||||||
channels and conversations, resolves the access-key participant, pins the
|
channels and conversations, resolves the access-key participant, pins the
|
||||||
public webhook URL to
|
public webhook URL to
|
||||||
@@ -80,6 +85,8 @@ public webhook URL to
|
|||||||
verifies webhooks while writes remain dark. Only then does one transaction
|
verifies webhooks while writes remain dark. Only then does one transaction
|
||||||
enable bootstrap readiness, the Control Plane, outbound messaging, and
|
enable bootstrap readiness, the Control Plane, outbound messaging, and
|
||||||
operational-action switches, followed by final readiness checks.
|
operational-action switches, followed by final readiness checks.
|
||||||
|
Any failure returns a non-zero exit code and re-disables the Bird capabilities
|
||||||
|
without stopping or restarting the core API.
|
||||||
It preserves existing valid credentials; otherwise it generates distinct
|
It preserves existing valid credentials; otherwise it generates distinct
|
||||||
48-byte random Control Plane and webhook secrets inside the container. The
|
48-byte random Control Plane and webhook secrets inside the container. The
|
||||||
webhook key remains backend-only.
|
webhook key remains backend-only.
|
||||||
|
|||||||
@@ -398,16 +398,16 @@ it('accepts staged activation only while every write capability remains disabled
|
|||||||
expect(bird_control_plane_auto_activation::stagedActivationReady($status))->toBeFalse();
|
expect(bird_control_plane_auto_activation::stagedActivationReady($status))->toBeFalse();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('wires fail-closed startup and a pinned local bootstrap without plaintext exposure', function (): void {
|
it('keeps Bird activation outside core API startup and pins local bootstrap without plaintext exposure', function (): void {
|
||||||
$repoRoot = getenv('PLENO_REPO_ROOT_FOR_TESTS');
|
$repoRoot = getenv('PLENO_REPO_ROOT_FOR_TESTS');
|
||||||
expect($repoRoot)->toBeString()->not->toBe('');
|
expect($repoRoot)->toBeString()->not->toBe('');
|
||||||
$start = file_get_contents($repoRoot . '/services/coolify/api/start.sh');
|
$start = file_get_contents($repoRoot . '/services/coolify/api/start.sh');
|
||||||
$local = file_get_contents($repoRoot . '/scripts/bird-control-plane-bootstrap-local.sh');
|
$local = file_get_contents($repoRoot . '/scripts/bird-control-plane-bootstrap-local.sh');
|
||||||
$route = file_get_contents(app_path('routes/birdControlPlaneRoute.php'));
|
$route = file_get_contents(app_path('routes/birdControlPlaneRoute.php'));
|
||||||
|
|
||||||
expect($start)->toContain('bird-control-plane-auto-activate.php')
|
expect($start)->not->toContain('bird-control-plane-auto-activate.php')
|
||||||
->and(strpos($start, 'bird-control-plane-auto-activate.php'))
|
->and($start)->toContain('php-fpm -D')
|
||||||
->toBeLessThan(strpos($start, 'php-fpm -D'))
|
->and($start)->toContain('exec nginx')
|
||||||
->and($local)->toContain(
|
->and($local)->toContain(
|
||||||
"bootstrap_url='https://api.truckwash.io:4433/bird/control-plane/v1/bootstrap'"
|
"bootstrap_url='https://api.truckwash.io:4433/bird/control-plane/v1/bootstrap'"
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user