Skip to Content
New release 12 available 🎉
LicensingCommand LineAI-Friendly Mode

AI-Friendly CLI Mode Server

Starting with Babel Licensing 11.8, the lic command line can emit a structured, machine-readable output stream suited to CI pipelines, order systems and AI agents, matching the mode the obfuscator gained in 11.7.

The default text output of lic is unchanged and remains fully backward compatible: existing scripts require no changes.

The AI-friendly mode is enabled by three global flags:

When --format=json or --format=ndjson is selected, stdout carries the structured stream and the human-readable messages are routed to stderr, so the two channels never interleave.

--format is also the option that selects the license output format (xml, serial, base32, ascii). The two value sets are disjoint, so both meanings can appear in one command line: lic MyApp.dll --format base32 --format json.

Flags

—format <format>

Selects the output format used on stdout. Allowed values:

ValueDescription
textDefault. Legacy human-readable console output.
jsonBuffered single JSON document written when the run completes (or on --help / --version). Best when stdout is captured to a file or piped into a JSON consumer.
ndjsonNewline-delimited JSON. One event per line, flushed as it is produced. Best for streaming consumers, log shippers and live agents.
lic MyApp.dll --keyfile keys.pem --sign --expiredate 365 --format json > result.json lic MyApp.dll --keyfile keys.pem --sign --format ndjson | jq -c 'select(.level=="error")'

With json or ndjson the logo banner is suppressed regardless of --logo, so that the first byte on stdout is valid JSON.

—quiet (-q)

Suppresses the copyright banner and every interactive prompt. The only prompt lic has is the password of a PFX key file: with --quiet, a missing --keypwd fails with an explicit error instead of blocking on input, which makes the flag safe for unattended runs on CI runners, in containers and from agents.

lic MyApp.dll --quiet --keyfile signing.pfx --keypwd "$KEY_PWD" --sign

--quiet is independent from --format: it can be combined with the text output to keep human-readable logs without the banner.

—strict-exit

Opts the process into semantic exit codes. Without this flag, lic preserves the legacy contract where 0 means success and 1 means any failure.

Exit codeConstantMeaning
0successThe command completed without errors.
10invalidArgumentsThe command line could not be parsed or contained an invalid value (for example an unknown option).
20inputNotFoundThe assembly, license file or another required input file was not found.
30processingFailureLicense processing failed: generation, update, or a signature that did not verify.
40licensingFailureThe tool’s own Babel Licensing license is missing, invalid or expired.
50keyOrSigningFailureA key file or container could not be loaded, or a cryptographic operation failed.

The numbers are the same as the obfuscator’s --strict-exit table, so one caller can drive babel and lic from a single exit-code table; the only difference is that code 30 means license processing here and obfuscation there. The classification is also surfaced as exitReason in the JSON result, even when --strict-exit is not active.

lic MyApp.licenses --verify --keyfile keys.pem --strict-exit --quiet echo "exit=$?"

Schema: lic.cli.v1

All structured payloads carry "schema": "lic.cli.v1" and a kind that tells what the document is: event, result, help or version. The schema mirrors the obfuscator’s babel.cli.v1 but is versioned separately, because the two tools expose different option sets. Within a major schema version, additive changes are allowed; breaking changes will bump the schema name.

Event record

Every message emitted while a run is in progress is mapped to an event:

event
{ "schema": "lic.cli.v1", "kind": "event", "ts": "2026-09-03T06:31:54.1686900Z", "level": "info", "message": "License saved to 'MyApp.licenses'" }
FieldTypeNotes
tsstring (ISO 8601, UTC)Timestamp at which the event was produced.
leveldebug | info | warning | errorSeverity.
codestringDiagnostic code, present only when the message has one.
messagestringHuman-readable message.
dataobjectOptional structured payload, present only when attached.

Result envelope

—format=json

A single JSON object is written to stdout when the run terminates. It contains the run-level summary and all events buffered during the run:

result (json)
{ "schema": "lic.cli.v1", "kind": "result", "exitCode": 0, "exitReason": "success", "elapsedMs": 133, "warnings": 0, "errors": 0, "events": [ { "schema": "lic.cli.v1", "kind": "event", "ts": "2026-09-03T06:31:54.1686900Z", "level": "info", "message": "License saved to 'MyApp.licenses'" } ] }

A failed run reports the failure class in exitReason and the error among the events. Here the input assembly did not exist:

result (json, failure)
{ "schema": "lic.cli.v1", "kind": "result", "exitCode": 20, "exitReason": "inputNotFound", "elapsedMs": 34, "warnings": 0, "errors": 1, "events": [ { "schema": "lic.cli.v1", "kind": "event", "ts": "2026-09-03T06:30:27.4366510Z", "level": "error", "message": "Error: The specified file '/build/missing.dll' was not found." } ] }

—format=ndjson

Each event is flushed as soon as it is produced, on its own line. After the last event, a final result line is appended, without an events array since the events have already been streamed:

result (ndjson)
{"schema":"lic.cli.v1","kind":"event","ts":"2026-09-03T06:31:54.3357790Z","level":"info","message":"License saved to 'MyApp.licenses'"} {"schema":"lic.cli.v1","kind":"result","exitCode":0,"exitReason":"success","elapsedMs":141,"warnings":0,"errors":0}
FieldTypeNotes
exitCodeintegerNumeric exit code actually returned by the process (legacy or strict, depending on --strict-exit).
exitReasonstringStable classification token. Always reflects the semantic class, even without --strict-exit.
elapsedMsintegerTotal wall-clock duration in milliseconds.
warningsintegerCount of warning-level events.
errorsintegerCount of error-level events.
eventsarrayPresent in json mode; omitted in ndjson.

Machine-readable —help

lic --help --format=json emits a complete description of every public option, suitable for tooling that needs to introspect the CLI surface: shell completions, AI agents, documentation generators.

--help --format=json (excerpt)
{ "schema": "lic.cli.v1", "kind": "help", "version": "12.0.0.0", "usage": "lic.exe <assembly|licensefile> [<other assemblies>...] [options]", "options": [ { "name": "trial", "aliases": [], "description": "Add or update trial license restriction", "args": "<key=value>", "argRequired": true, "argOptional": false, "negatable": false, "incremental": false, "container": "hash", "type": "string", "group": "- License Restrictions -", "details": "Set trial restriction properties entering key-value pairs:\n\nid=<id> Restriction id\nexpire=<date> Set expiration date\ndays=<n> Set number of trial days\n..." } ] }
FieldMeaning
name, aliasesThe option name and its alternative spellings, including negated forms and single-letter aliases.
argsThe argument placeholder as shown in the usage, or empty for a switch.
argRequired, argOptionalWhether the option takes a mandatory or an optional argument.
negatableWhether the option accepts the no prefix.
containerscalar for a single value, array for a repeatable option, hash for key=value pairs.
typestring or integer.
groupThe section of the help output the option belongs to.
detailsThe extended description, the same text lic --help <option> prints.

A scoped form is also supported: lic --help trial --format=json emits the same envelope containing only the trial option. Hidden and internal options are never returned.

Machine-readable —version

lic --version --format=json emits product, version, runtime and platform information:

--version --format=json
{ "schema": "lic.cli.v1", "kind": "version", "product": "Babel Licensing", "version": "12.0.0.0", "fileVersion": "12.0.0.1", "runtime": ".NET 10.0.0", "platform": "osx-arm64" }

The platform field uses the .NET runtime identifier (RID) on .NET 5 and later, and a synthetic <os>-<arch> string on legacy targets.

Driving lic From an Agent

An agent that has never seen lic can operate it from the JSON help alone: read the option schema, build the command line from the args and container hints, run with --format json --quiet --strict-exit, and branch on exitReason. A typical loop for issuing a license looks like this:

lic --help --format json > lic-options.json lic MyApp.dll --keyfile keys.pem --sign --licensee name="Contoso" --expiredate 365 --output MyApp.licenses --format json --quiet --strict-exit > result.json jq -r '.exitReason' result.json

The same contract, exit codes and flag names apply to the obfuscator command line, documented in AI-Friendly Mode of the obfuscator manual, so one integration covers both tools.

Backward compatibility

  • The default value of --format is text; the legacy textual output, exit codes and behavior are unchanged when no new flag is specified.
  • Without --strict-exit, the process keeps returning 0 on success and 1 on any failure, so existing gates that branch on $? keep working.
  • --quiet is independent and can be opted into without enabling structured output.
Last updated on