Tooling

CLI

One terminal tool for setting up SDK integrations, verifying configurations, shipping OTA releases (Flutter + React Native), managing flags + config + symbols, and seeding demos.

The Sankofa CLI is a Node-based command-line tool published as sankofa-cli on npm with binary name sankofa. It handles project-level operations: SDK setup, integration verification, OTA releases for both Flutter and React Native, feature flag and config management, error-tracking symbol uploads, signing-key management, and demo seeding.

Flutter ships Dart code patches; React Native ships JavaScript bundles — both App Store + Play Store compliant. Same release / patch / deploy syntax for both — the CLI detects the stack and runs the right pipeline.

Install

bash
npm install -g sankofa-cli

Requirements

  • Node.js 18+
  • For iOS builds: macOS + Xcode
  • For Android builds: Android SDK + JDK 17

Quick start

bash
# 1. Log in to your Sankofa account (one-time)
sankofa login

# 2. Set up the project — auto-detects platform, patches native files
sankofa init                # interactive picker
sankofa init --deploy       # install Sankofa Deploy
sankofa init --catch        # install Sankofa Catch
sankofa init --all          # install everything available for this stack

# 3. Verify everything is wired up
sankofa doctor

For Flutter (OTA patches):

bash
sankofa init --deploy            # scaffold the whole Deploy setup in a Flutter project
sankofa release android          # ship the first base release
sankofa patch android            # push a Dart-only patch
sankofa patch ios                # push an iOS patch (App Store compliant)

For React Native (analytics + OTA deploy):

bash
sankofa release ios              # ship the first base release
sankofa patch ios                # push a JS-only patch

Top-level commands

The CLI exposes these top-level commands:

CommandPurpose
initSet up Sankofa in any project. Auto-detects platform; pass --deploy/--catch/--flag/--config/--all to skip the picker.
checkVerify SDK integration is correct.
doctorLow-level toolchain diagnostics (Node, Xcode, Java, Flutter, Sankofa native wiring).
login / logoutBrowser-based or CI-token authentication.
switchSwitch the active project / environment.
releaseBuild + publish a base release (Flutter or React Native).
patchShip a code-only OTA patch against an existing base release.
deploySmart deploy — auto-picks release (first time) or patch (subsequent).
previewDownload + install a published release locally.
statusShow all releases for the current project.
releases / patchesList + manage releases / patches (rollout, kill-switch, mandatory).
rulesManage deployment gating rules.
scheduleManage rollout schedules.
defaultsManage default configurations per project.
distBuild the signed store binary (no OTA).
submitUpload signed binary to App Store Connect / Play Console.
keysManage Sankofa Deploy signing keys.
engineManage the cached Sankofa Flutter runtime.
flagsManage feature flags (Switch).
configManage remote config items.
catchCatch / error-tracking triage + symbol upload.
demoSeed demo flags + config items.
updateUpdate everything Sankofa knows about — CLI, bundled Flutter, engine cache, project SDK.
upgradeUpdate the CLI binary to the latest npm release.

Setup commands

sankofa init

Idempotent — safe to re-run. Creates .sankofa.json, updates .gitignore, and handles platform-specific native setup. Re-running on an already-configured project skips each step that's already in place (it does not duplicate or overwrite).

bash
sankofa init                                              # interactive product picker
sankofa init --deploy                                     # install Sankofa Deploy (OTA)
sankofa init --catch                                      # install Sankofa Catch (errors)
sankofa init --flag                                       # install Sankofa Switch (flags)
sankofa init --config                                     # install Sankofa Config
sankofa init --all                                        # install everything available for this stack
sankofa init --endpoint https://api.sankofa.dev --project-id proj_...
sankofa init --force                                      # overwrite existing .sankofa.json

For Flutter, sankofa init --deploy does the full setup automatically (see the Flutter SDK page): adds the Sankofa Flutter SDK, creates sankofa.yaml, wires lib/main.dart, and patches the Android + iOS native files. Then run sankofa login — it fills sankofa.yaml's app_id and api_key from the project you select, so there's nothing to paste. init and login are order-independent; whichever you run first, the other completes the setup.

sankofa check

Verifies SDK integration with detailed pass/fail output:

bash
sankofa check              # all modules
sankofa check analytics    # analytics-specific
sankofa check deploy       # deploy-specific
                         #   • React Native: full SDK / native / credentials / server check
                         #   • Flutter: defers to `sankofa doctor` (richer Flutter-specific checks)

sankofa doctor

Toolchain + integration diagnostics. Reports Node, Xcode, Java/Gradle, Flutter SDK, Android SDK, server reachability, and the full Sankofa Deploy wiring for Flutter projects (sankofa.yaml, pubspec dependency, native meta-data, MainActivity / AppDelegate, bundled Sankofa Flutter SDK + runtime cache state).

bash
sankofa doctor             # full toolchain + integration check
sankofa doctor --deploy    # focus on Sankofa Deploy wiring only

Authentication

sankofa login

Browser-based OAuth-style login. Creates a Deploy Token bound to the selected project.

bash
sankofa login
sankofa login --deploy-token sk_deploy_... --project-id proj_...   # CI/CD
sankofa login --region eu                                          # pin EU residency

sankofa logout / sankofa switch

bash
sankofa logout              # both project + global scopes
sankofa logout --project    # project-scoped only
sankofa switch              # change to a different project without re-auth

Deploy commands (Flutter + React Native)

The same release / patch / deploy / preview / status commands work for both Flutter and React Native projects — the CLI detects the stack from your pubspec.yaml / package.json and runs the right pipeline.

sankofa release

Build and publish a base release — signed native binary + OTA-ready payload in one step.

bash
# Flutter
sankofa release android                        # produces an AAB by default
sankofa release android --apk                  # produce an APK (sideload-installable)
sankofa release ios                            # produces a signed .ipa for App Store
sankofa release android --publish --rollout 50 --description "v1.2.0"
sankofa release --dry-run android              # build + run the safety check locally, no upload

# Flavored apps (gradle flavors + per-flavor entrypoint)
sankofa release android --flavor staging -t lib/main_staging.dart
sankofa release ios --flavor staging -t lib/main_staging.dart

# Also upload an installable preview build (see preview --from-server)
sankofa release android --preview-artifact

# React Native (identical syntax)
sankofa release ios
sankofa release android --publish --rollout 50 --description "v1.2.0"

For Flutter, iOS builds a signed .ipa for App Store Connect / TestFlight (or just the .xcarchive with --no-codesign) and Android builds your .aab (or --apk); both register the release so sankofa patch can ship code updates on top.

Useful flags:

  • --flavor <name> / -t, --target <file> — Flutter product flavor + per-flavor entrypoint (e.g. --flavor staging -t lib/main_staging.dart). Required for flavored apps.
  • --no-codesign — iOS: build the .xcarchive without signing (sign + export later in Xcode).
  • --preview-artifact — also build + upload an installable preview build so teammates can run this release with sankofa preview --from-server. Android (APK) for Flutter + React Native; an iOS simulator app for React Native only. Flutter iOS isn't supported (a physical iPhone can't install a downloaded build) — use sankofa preview ios or TestFlight.
  • --apk / --appbundle — Android format selector (Flutter + RN).
  • --dry-run — build locally + capture the patch-safety-check baseline, but do not contact the server.
  • --upload-mapping <path> / --upload-dsym <path> / --upload-dart-symbols <path> — attach Android mapping / iOS dSYMs / Flutter symbols for Catch symbolication.

sankofa patch

Ship a code-only update against an existing base release. No native build, no App Store / Play Store resubmission.

bash
# Flutter — code-only patch
sankofa patch android
sankofa patch ios                              # iOS — App Store compliant
sankofa patch android --publish --rollout 25 --description "fix crash on Pixel 6"
sankofa patch ios --target-binary-version 1.2.0 --label hotfix-12
sankofa patch ios -t lib/patch_staging.dart    # pick a specific patch entry-point

# React Native — JS bundle patch
sankofa patch ios --publish --mandatory
sankofa patch android --rollout 50

Flutter options:

  • -t, --target <file> — the patch entry-point to compile (default lib/sankofa_patch.dart). Use it when you keep several patch entries and want to ship one; --entry-file is an alias.
  • --target-binary-version <semver> — match the host app's published version exactly (pubspec.yaml.version).
  • --label <label> — patch label override.

sankofa deploy

Smart wrapper that picks release (no prior release exists) or patch (subsequent runs) automatically. Useful for one-shot CI scripts.

bash
sankofa deploy android
sankofa deploy ios --rollout 50
sankofa deploy --dry-run android

sankofa preview / sankofa status

preview runs your app for QA. For React Native it downloads + installs a published release on a simulator/emulator. For Flutter there are two modes:

  • Local run (default) — runs your current source on a device via the Sankofa Flutter runtime, passing --flavor, -t/--target, -d/--device, and the build mode (--release/--profile/--debug).
  • From server (--from-server, or implied by --label/--version) — Android only: downloads a published release and installs it on an Android device/emulator, so a tester can run a specific build without your source. Requires the release to have been published with --preview-artifact. iOS can't install a downloaded build on a device — use Local run for QA or TestFlight for on-device.
bash
# React Native — install a published release on a simulator
sankofa preview ios
sankofa preview ios --label v1.2.0-patch.3

# Flutter — local run (your current source)
sankofa preview -d <device-id> --flavor staging -t lib/main_staging.dart

# Flutter — run a published Android release from the server (no source needed)
sankofa preview android --from-server --label v1.2.0 -d <adb-serial>
# (iOS has no server preview — run 'sankofa preview ios' locally, or use TestFlight)

# Dashboard of releases
sankofa status
sankofa status --env live --platform ios

sankofa releases / sankofa patches

Read-only listing of releases / patches. Useful for CI scripts that need to fetch the current release SHA without dashboard access.

sankofa rules / sankofa schedule / sankofa defaults

Manage deployment-time gating: rules to halt rollouts, schedules for time-based releases, default configurations per project.

sankofa dist / sankofa submit

bash
# Build only the signed store binary, no OTA
sankofa dist ios
sankofa dist android --android-format aab

# Upload signed binary to App Store Connect or Play Console
sankofa submit ios --apple-api-key-id ABC --apple-api-issuer UUID
sankofa submit android --google-service-account ~/sa.json --google-track internal

Flutter Deploy: signing keys + engine

These commands are Flutter-specific and become relevant once you've run sankofa init --deploy. The setup writes .sankofa/flutter-version (a pin file) and sankofa.yaml (project keys) that the commands below read automatically.

sankofa keys — signing keys

Every Flutter patch is signed with the project's private key, and the server + SDK both verify the signature before applying. Keys are project-scoped — generate one per project and back up the private key.

bash
sankofa keys generate                          # generate a keypair for the current project
sankofa keys generate --force                  # overwrite (invalidates already-shipped patches)
sankofa keys show                              # print the base64 public key (embed in host app or server)
sankofa keys path                              # print the local private key path (for backup)
sankofa keys register                          # POST the public key to the server so it verifies patch signatures at upload time

Keys are stored at ~/.config/sankofa/keys/<project_id>.ed25519. Add the directory to your secrets manager — losing the key means you can't ship signed patches against existing releases without a coordinated rotation.

sankofa engine — Sankofa Flutter runtime

The CLI manages the Sankofa Flutter runtime + bundled SDK locally, verifying integrity against the registry on each download.

bash
sankofa engine list                            # what's installed + what's available
sankofa engine download                        # download the runtime for the current project's pinned version
sankofa engine install [version]               # install the bundled Sankofa Flutter SDK + runtime in one shot
sankofa engine verify                          # re-verify the cached runtime against the registry
sankofa engine path                            # print the cache root (useful in shell scripts)

The cache lives at ~/.sankofa/engines/. sankofa init --deploy already runs engine install for you — the explicit commands are for upgrades and CI cache priming.

sankofa update — keep the whole stack fresh

The full Sankofa stack on a developer machine has four moving parts: the CLI itself, the bundled Sankofa Flutter SDK, the engine cache, and the per-project pub.dev SDK. update checks each one in a single sweep.

bash
sankofa update                                 # check + apply updates to all four surfaces
sankofa update --check                         # dry-run — report what would be updated, change nothing
sankofa update --only cli                      # bump the CLI only
sankofa update --only flutter                  # refresh the bundled Sankofa Flutter SDK only
sankofa update --only engine                   # refresh the engine cache only
sankofa update --only sdk                      # `flutter pub upgrade sankofa_flutter` in the current project

sankofa upgrade — bump the CLI binary itself

Just the CLI — update covers the broader stack; upgrade is the focused "newer version of sankofa-cli on npm" check.

bash
sankofa upgrade                                # check + install if a newer version exists
sankofa upgrade --check                        # dry-run — report only
sankofa upgrade --sudo                         # use sudo for the install (CI scripts that already plan for it)

macOS perms note. On a default macOS setup the global npm prefix is /usr/local/lib/node_modules (root-owned), so npm install -g … fails with EACCES. sankofa upgrade detects that case and auto-retries the install under sudo — you'll be prompted for your password on stdin once, then the upgrade proceeds. In non-interactive contexts (CI, piped output) the CLI prints the exact remedy instead of popping a hidden password prompt:

text
sudo npm install -g sankofa-cli@latest
  (or: sankofa upgrade --sudo)

A user-writable npm prefix (e.g. via nvm) avoids the sudo retry altogether.

Switch commands

Flag CRUD authenticates with your dashboard JWT (not the Deploy Token) — flag operations need full user + project-role RBAC.

Flag subcommands

bash
sankofa flags list                                                # list all flags
sankofa flags list --env test --include-archived                  # include archived
sankofa flags get new_checkout                                    # show full details

sankofa flags create new_checkout --description "..." --default false

# Progressive rollout
sankofa flags toggle new_checkout 10                              # 10%
sankofa flags toggle new_checkout 50                              # 50%
sankofa flags toggle new_checkout 100                             # full

# Kill switch
sankofa flags halt new_checkout --reason "spike in errors"
sankofa flags resume new_checkout

sankofa flags archive new_checkout                                # archive

# Stale-flag scanner
sankofa flags scan
sankofa flags scan --strict                                       # fail CI on warnings

The stale-flag scanner walks your code for .getFlag(...) and .getVariant(...) calls across JS/TS/Dart/Swift/Kotlin and cross-references the keys against the server.

Config commands

bash
sankofa config list
sankofa config list --env test
sankofa config get max_upload_mb

# Type is required on create; updates require matching type.
sankofa config set max_upload_mb int 25 --description "Max file upload"
sankofa config set support_url string "https://support.acme.com"
sankofa config set trial_discount_pct float 0.2
sankofa config set maintenance_banner_enabled bool true
sankofa config set pricing json '{"pro":9.99,"growth":49}'

sankofa config history max_upload_mb
sankofa config history max_upload_mb --limit 5

# Non-destructive — writes a new version whose snapshot matches the target.
sankofa config rollback max_upload_mb 3 --note "revert accidental bump"

Catch commands

Error-tracking triage and symbol management:

bash
# Issue triage
sankofa catch issues list
sankofa catch issues get isu_abc123
sankofa catch issues assign isu_abc123 --to [email protected]

# Event inspection
sankofa catch events list --issue isu_abc123
sankofa catch events get evt_xyz789

# Alerts
sankofa catch alerts list
sankofa catch alerts test alt_def456

# Symbol upload (sourcemaps, dSYM, ProGuard mapping, NDK, Flutter)
sankofa catch symbols upload --kind js_sourcemap --release "$RELEASE_SHA" --dir ./dist
sankofa catch symbols upload --kind dsym --release "$RELEASE_SHA" --dir ./build/dSYMs
sankofa catch symbols upload --kind proguard_mapping --release "$RELEASE_SHA" --file ./build/outputs/mapping/release/mapping.txt
sankofa catch symbols upload --kind ndk --release "$RELEASE_SHA" --dir ./obj
sankofa catch symbols upload --kind flutter --release "$RELEASE_SHA" --dir ./build/symbols

sankofa catch symbols list --release "$RELEASE_SHA"
sankofa catch symbols delete --kind js_sourcemap --id sym_abc

# Statistics
sankofa catch stats --since 7d

# Reverse-lookup obfuscated stacks
sankofa catch symbolicate --release "$RELEASE_SHA" --stack ./crash.txt

# Manifest helpers (used by CI integrations)
sankofa catch make-dsym-manifest --dir ./build/dSYMs
sankofa catch make-ndk-manifest --dir ./obj

The symbols upload subcommand replaces what was historically a top-level sourcemaps upload — there's no separate sourcemaps command today.

Demo fixtures

sankofa demo seed

bash
sankofa demo seed
sankofa demo seed --force           # overwrite existing values
sankofa demo seed --skip-flags      # only seed config items
sankofa demo seed --skip-config     # only seed feature flags

Creates 6 flags + 6 config items used by every Sankofa example app — new_home_layout, checkout_cta_variant, onboarding_v2_rollout, ai_summary_kill_switch, ab_pricing_page, premium_badge_visible (flags) and support_url, max_uploads_per_day, trial_discount_pct, maintenance_banner_enabled, pricing_table, theme_colors (config).

Authentication resolution

Credentials are resolved in this order (highest wins):

  1. Environment variables

    SANKOFA_DEPLOY_TOKEN, SANKOFA_JWT, SANKOFA_PROJECT_ID, SANKOFA_ENDPOINT, SANKOFA_ENVIRONMENT.

  2. Project config

    .sankofa.json in the project root (created by sankofa init).

  3. Global config

    ~/.sankofa/credentials.json (written by sankofa login).

Environment variables

VariablePurpose
SANKOFA_DEPLOY_TOKENCI-friendly Deploy auth token.
SANKOFA_JWTCI-friendly dashboard JWT.
SANKOFA_PROJECT_IDProject ID.
SANKOFA_ENDPOINTServer base URL.
SANKOFA_ENVIRONMENTlive or test.

Build artifact identifiers

FieldValue
npm packagesankofa-cli
Binary namesankofa
Versionlatest on npm

What's next

Edit this page on GitHub