Push CLI

Last updated on Jul 12, 2026

Current Status

Push has a first-class CLI command group for local payload validation, Firestore-backed token resolution, and guarded send attempts.

Use the CLI for repeatable QA checks that do not deliver notifications, plus explicitly labeled simulated sends. Non-dry-run sends require credentials and produce per-target HTTP results. Real FCM delivery is still not claimed until an approved live project/device fixture proves it.

Commands

push validate --payload payload.json
push validate --payload payload.json --project demo-firestruct
push resolve-tokens --source token-source.json
push resolve-tokens --collection users --field "push.tokens.*" --path-pattern "users/*"
push send --payload payload.json --confirm --dry-run
push send --payload payload.json --confirm --access-token "$GOOGLE_ACCESS_TOKEN"

The push module also supports selected-module routing:

module select push
run validate --payload payload.json
run resolve-tokens --source token-source.json

Payload File

push validate --payload reads a JSON object. Common fields:

{
  "targetMode": "token",
  "tokens": ["abcDEF1234567890"],
  "title": "Hello",
  "body": "World",
  "data": {
    "kind": "smoke"
  },
  "dryRun": true
}

Validation returns JSON with:

  • valid
  • validation errors and warnings
  • messagePreview
  • deliveryMode: "validation-only"
  • sendImplemented: false

Validation covers local request shape and payload safety only. It reports APNS badge, priority, push type, expiration, and background-update issues; Android priority, TTL, notification priority, visibility, and color issues; Webpush TTL, urgency, and link issues; invalid notification image/icon/badge URLs; reserved FCM data keys; oversized data fields; and the FCM HTTP v1 4096-byte message limit.

No FCM send is attempted by push validate.

Send Boundary

push send requires a selected project and --confirm. Non-dry-run delivery is rejected for read-only projects, and production non-dry-run delivery requires --production-confirm SEND.

Without --authorization or --access-token, push send does not contact FCM. It returns deliveryMode: "simulated-send", per-token status: "simulated", fcmAPIAttempted: false, liveDeliveryAttempted: false, and task output.

With credentials, push send uses the FCM HTTP v1 API. --dry-run sends validate_only: true and reports deliveryMode: "fcm-http-v1-dry-run". Non-dry-run credentialed sends report deliveryMode: "fcm-http-v1-send" and must not be claimed as release evidence until an approved live FCM project and device/token fixture are recorded.

Token Source File

push resolve-tokens --source reads a JSON object:

{
  "collection": "users",
  "pathPattern": "users/*",
  "tokenFieldPath": "push.tokens.*",
  "limit": 500
}

Resolution queries the selected Firebase project with Firestruct's Firestore query service, then passes the real returned documents into PushTokenSourceResolverService. Output includes resolved tokens, matched document paths, invalid token candidates, and logs.

Still Live-Only

Actual FCM delivery proof remains live-only. Release runbooks may not claim real delivery support until an approved FCM project/device fixture captures the API response and cleanup or token invalidation notes.

Verification

Covered by:

swift test --filter FirestructCLIIntegrationTests/testPushValidateUsesPayloadJSONAndSelectedModuleRouting
swift test --filter FirestructCLIIntegrationTests/testCLICommandsRoundTripAgainstFirebaseEmulators

The focused validation test reads real valid and invalid payload files, validates locally, checks warnings, platform/data/URL errors, message preview JSON, and verifies selected-module run validate. It also verifies push send rejects missing --confirm, returns labeled simulated per-token results with task counters for a confirmed no-credential dry-run, rejects read-only non-dry-run delivery, and requires --production-confirm SEND for production non-dry-run sends. The emulator round-trip writes token documents into a real Firestore emulator, resolves tokens via a JSON token-source file, reports invalid candidates, and verifies selected-module run resolve-tokens.

Source Anchors

  • Sources/FirestructCLIKit/PushCommand.swift
  • Sources/FirestructCore/PushTestingService.swift
  • Sources/FirestructCore/PushTokenSourceResolverService.swift