Skip to content

CLI Reference

Sharp Ninja edited this page May 16, 2026 · 1 revision

CLI Reference: flagctl

flagctl is the SharpNinja Feature Flags command-line tool. It validates feature flag manifests at build time and CI time, checking structure, CEL rule syntax, Ed25519 signature material, product identity, schema version compatibility, and generated accessor/gate bindings.


Installation

Install as a global .NET tool:

dotnet tool install --global SharpNinja.FeatureFlags.Cli

Install as a local tool in a repository:

dotnet tool install SharpNinja.FeatureFlags.Cli

After installing locally, invoke via:

dotnet flagctl validate flags/flags.json

Verify the installation:

flagctl --help

Commands

flagctl validate

flagctl validate <manifest-path> [options]

Validates one JSON feature flag manifest file. The manifest path must point to an existing, readable file (not a directory).

Full usage:

Usage: flagctl validate <manifest-path> [options]

Commands:
  validate <manifest-path>  Validate one JSON feature-flag manifest.

Options:
  --product-id <id>         Require the manifest productId to match the build ProductId.
  --release-id <id>         Require the manifest releaseId to match the build ReleaseId.
  --schema-version <value>  Supported manifest schema version. Defaults to 1.
  --public-key <path>       Validate embedded Ed25519 public-key material.
  --generated-bindings <path>
                            Validate generated accessor/gate binding diagnostics input.
  -h, --help                Show usage.

Options Reference

Option Argument Description
--product-id <id> When provided, the manifest's productId field must exactly match this value. Emits FFMANIFEST_PRODUCT_MISMATCH on mismatch.
--release-id <id> When provided, the manifest's releaseId field must exactly match this value. Emits FFMANIFEST_RELEASE_MISMATCH on mismatch.
--schema-version <integer> The highest manifest schema version this tooling version supports. Defaults to 1. Emits FFMANIFEST_SCHEMA_COMPATIBILITY when the manifest requires a higher version.
--public-key <path> Path to a file containing a raw 32-byte Ed25519 public key, or a base64-encoded 32-byte Ed25519 public key. Validates the format of the key file.
--generated-bindings <path> Path to a generated bindings JSON file (output of the SDK source generator). May be specified multiple times to validate multiple binding files. Emits SNFF0001, SNFF0002, and SNFF0003 diagnostics.
-h, --help (none) Print usage and exit with code 0.

Exit Codes

Code Meaning
0 Validation passed; the manifest is valid.
1 Validation failed; one or more diagnostic errors were emitted to stderr.
2 Usage error; the command was invoked incorrectly (unknown command, missing manifest path, unreadable file, unknown option).

Diagnostic Codes

SDK-level diagnostics (generated bindings)

Code Description Fix
SNFF0001 A generated binding references a flag key that does not exist in the manifest's flags array. Add the missing flag to the manifest, or remove the stale binding from the generated bindings file.
SNFF0002 A generated accessor's CLR type or a generated gate's kind does not match the manifest flag type. For example, a gate binding requires a boolean flag but the manifest declares the flag as string. Align the flag type in the manifest with the CLR type used in the generated binding, or change the binding kind.
SNFF0003 A generated binding's productId is not listed in the flag's productScope in the manifest. Add the product to the flag's productScope in the manifest, or correct the productId in the generated binding.

Manifest structure diagnostics

Code Description Fix
FFMANIFEST_CEL_SYNTAX A targeting rule's when expression is not valid CEL syntax. Correct the CEL expression in the manifest.
FFMANIFEST_PRODUCT_MISMATCH The manifest productId does not match --product-id. Ensure the manifest is for the correct product, or update --product-id to match.
FFMANIFEST_RELEASE_MISMATCH The manifest releaseId does not match --release-id. Regenerate the manifest with the correct release identifier.
FFMANIFEST_SCHEMA_COMPATIBILITY The manifest schemaVersion or compatibility.minimumReaderSchemaVersion exceeds --schema-version. Upgrade flagctl to a version that supports the required schema version.
FFMANIFEST_SIGNATURE_REQUIRED The manifest does not contain a signature object. Add a signature block (generated by the signing step of the publish workflow).
FFMANIFEST_SIGNATURE_OBJECT The signature field is present but is not a JSON object. Fix the manifest structure so signature is an object.
FFMANIFEST_SIGNATURE_ALGORITHM signature.algorithm is not Ed25519. Only Ed25519 signatures are supported in v1.
FFMANIFEST_SIGNATURE_KEYID signature.keyId is missing or blank. Populate signature.keyId with the identifier of the signing key.
FFMANIFEST_SIGNATURE_VALUE signature.value is not a valid base64-encoded 64-byte Ed25519 signature. Re-sign the manifest with a valid Ed25519 private key.
FFMANIFEST_PUBLIC_KEY_READ The file supplied via --public-key could not be read. Verify the path and file permissions.
FFMANIFEST_PUBLIC_KEY_FORMAT The public key file does not contain a valid raw or base64-encoded 32-byte Ed25519 key. Export the Ed25519 public key as raw 32 bytes or as a base64-encoded string.
FFMANIFEST_FILE_READ The manifest file could not be read (permission denied or I/O error). Verify the path and file permissions. Exits with code 2.
SNFF_BINDING_JSON_INVALID A generated bindings file is not valid JSON. Regenerate the bindings file, or check for corruption.
SNFF_BINDING_SCHEMA A generated bindings file is missing the required bindings array, or the file could not be read. Regenerate the bindings file from the SDK source generator.

Example Output

Valid manifest

$ flagctl validate flags/flags.json \
    --product-id TruckMate \
    --release-id 1.2.0 \
    --public-key flags/public-key.ed25519

valid: flags/flags.json

Exit code: 0

Invalid manifest with errors

$ flagctl validate flags/flags.json \
    --product-id TruckMate \
    --release-id 1.2.0 \
    --generated-bindings obj/SharpNinjaFeatureFlagsBindings.json

error FFMANIFEST_SIGNATURE_REQUIRED $.signature: manifest must include a signature object for v1 bundled defaults.
error SNFF0001 obj/SharpNinjaFeatureFlagsBindings.json:$.bindings[2].flagKey: generated binding references unknown flag key 'enable-old-feature'.
error SNFF0003 obj/SharpNinjaFeatureFlagsBindings.json:$.bindings[0].productId: generated binding productId 'DriverMate' is not in productScope for flag 'enable-dark-mode'.

Exit code: 1

Usage error

$ flagctl

Usage: flagctl validate <manifest-path> [options]

Commands:
  validate <manifest-path>  Validate one JSON feature-flag manifest.
...

Exit code: 2

File not found

$ flagctl validate missing/flags.json

error: manifest file not found: missing/flags.json

Exit code: 2


CI Integration

Run flagctl as a step in your CI pipeline. Exit code 1 causes the pipeline step to fail.

GitHub Actions

- name: Install flagctl
  run: dotnet tool install --global SharpNinja.FeatureFlags.Cli

- name: Validate feature flag manifest
  run: |
    flagctl validate flags/flags.json \
      --product-id "${{ env.PRODUCT_ID }}" \
      --release-id "${{ env.RELEASE_ID }}" \
      --public-key flags/public-key.ed25519 \
      --schema-version 1

Azure Pipelines

- script: dotnet tool install --global SharpNinja.FeatureFlags.Cli
  displayName: Install flagctl

- script: |
    flagctl validate flags/flags.json \
      --product-id "$(ProductId)" \
      --release-id "$(ReleaseId)" \
      --public-key flags/public-key.ed25519
  displayName: Validate feature flag manifest
  failOnStderr: false  # flagctl writes errors to stderr; rely on exit code

flagctl writes diagnostic errors to stderr and the valid: confirmation to stdout. Set failOnStderr: false and let the exit code govern the step result.


MSBuild Integration

When a project references the SharpNinja.FeatureFlags.Build NuGet package, flagctl is invoked automatically during dotnet build as part of the ValidateSharpNinjaFeatureFlagManifest target. No manual CI step is needed as long as flagctl is on the PATH.

How it works

The buildTransitive/SharpNinja.FeatureFlags.Build.targets file shipped with the Build package injects two targets before CoreCompile:

  1. ValidateSharpNinjaFeatureFlagBuildProperties - Fails the build if ProductId or ReleaseId MSBuild properties are missing when a manifest file is present.
  2. ValidateSharpNinjaFeatureFlagManifest - Calls flagctl validate with arguments derived from MSBuild properties.

MSBuild properties

Property Default Description
SharpNinjaFeatureFlagsManifest $(MSBuildProjectDirectory)\flags\flags.json Path to the manifest file
SharpNinjaFeatureFlagsPublicKey $(MSBuildProjectDirectory)\flags\public-key.ed25519 Path to the Ed25519 public key
SharpNinjaFeatureFlagsValidateOnBuild true Set to false to disable auto-validation
SharpNinjaFeatureFlagsCliCommand flagctl Command used to invoke the CLI
SharpNinjaFeatureFlagsSchemaVersion 1 Passed to --schema-version
SharpNinjaFeatureFlagsRequirePublicKey true Fails the build if the public key file is missing
SharpNinjaFeatureFlagsEmitBuildIdentity true Stamps ProductId and ReleaseId as AssemblyMetadata attributes

The effective MSBuild invocation is equivalent to:

flagctl validate "flags/flags.json" \
  --product-id "$(ProductId)" \
  --release-id "$(ReleaseId)" \
  --schema-version "1" \
  --public-key "flags/public-key.ed25519" \
  --generated-bindings "path/to/binding1.json" \
  --generated-bindings "path/to/binding2.json"

Generated binding files are included by adding items to the SharpNinjaFeatureFlagBinding item group:

<ItemGroup>
  <SharpNinjaFeatureFlagBinding Include="$(IntermediateOutputPath)FeatureFlagBindings.json" />
</ItemGroup>

Disabling validation for a specific build

<PropertyGroup>
  <SharpNinjaFeatureFlagsValidateOnBuild>false</SharpNinjaFeatureFlagsValidateOnBuild>
</PropertyGroup>

Or on the command line:

dotnet build -p:SharpNinjaFeatureFlagsValidateOnBuild=false

Clone this wiki locally