nbc supports streaming JSON Lines (JSONL) output for machine-readable consumption by installers, update tools, and automation systems. This document describes the output format and how to consume it.
Enabling JSON Output
Add the --json flag to any command:
nbc list --json
nbc install --image myimage:latest --device /dev/sda --json
nbc update --json
nbc status --json
nbc validate --device /dev/sda --json
Output Format
Simple Commands (list, status, validate)
For simple query commands, nbc outputs a single JSON object with the complete result:
nbc list --json
{
"disks": [
{
"device": "/dev/sda",
"size": 500107862016,
"size_human": "465.8 GB",
"model": "Samsung SSD 860",
"is_removable": false,
"partitions": [
{
"device": "/dev/sda1",
"size": 536870912,
"size_human": "512.0 MB",
"mount_point": "/boot/efi",
"filesystem": "vfat"
}
]
}
]
}
Streaming Commands (install, update)
For long-running operations, nbc outputs JSON Lines (one JSON object per line) to provide real-time progress updates:
nbc install --image myimage:latest --device /dev/sda --json
{"type":"message","timestamp":"2025-12-19T10:30:00Z","message":"Checking prerequisites..."}
{"type":"message","timestamp":"2025-12-19T10:30:01Z","message":"Validating disk /dev/sda..."}
{"type":"step","timestamp":"2025-12-19T10:30:02Z","step":1,"total_steps":6,"step_name":"Creating partitions"}
{"type":"message","timestamp":"2025-12-19T10:30:03Z","message":"Created EFI partition"}
{"type":"step","timestamp":"2025-12-19T10:30:10Z","step":2,"total_steps":6,"step_name":"Formatting partitions"}
{"type":"step","timestamp":"2025-12-19T10:30:20Z","step":3,"total_steps":6,"step_name":"Mounting partitions"}
{"type":"step","timestamp":"2025-12-19T10:30:21Z","step":4,"total_steps":6,"step_name":"Extracting container filesystem"}
{"type":"step","timestamp":"2025-12-19T10:31:00Z","step":5,"total_steps":6,"step_name":"Configuring system"}
{"type":"step","timestamp":"2025-12-19T10:31:10Z","step":6,"total_steps":6,"step_name":"Installing bootloader"}
{"type":"complete","timestamp":"2025-12-19T10:31:20Z","message":"Installation complete! You can now boot from this disk.","details":{"image":"myimage:latest","device":"/dev/sda","filesystem":"ext4"}}
Event Types
Each JSON line contains a type field indicating the event type:
| Type | Description |
|---|---|
step |
Start of a numbered step in the operation |
progress |
Progress update within a step (with percentage) |
message |
Informational message |
warning |
Warning that doesn't stop the operation |
error |
Error that caused the operation to fail |
complete |
Successful completion of the entire operation |
Event Schema
Step Event
{
"type": "step",
"timestamp": "2025-12-19T10:30:02Z",
"step": 1,
"total_steps": 6,
"step_name": "Creating partitions"
}
| Field | Type | Description |
|---|---|---|
type |
string | Always "step" |
timestamp |
string | ISO 8601 timestamp (UTC) |
step |
integer | Current step number (1-based) |
total_steps |
integer | Total number of steps |
step_name |
string | Human-readable step description |
Progress Event
{
"type": "progress",
"timestamp": "2025-12-19T10:30:30Z",
"percent": 45,
"message": "Extracting layer 3/5"
}
| Field | Type | Description |
|---|---|---|
type |
string | Always "progress" |
timestamp |
string | ISO 8601 timestamp (UTC) |
percent |
integer | Progress percentage (0-100) |
message |
string | Optional progress description |
Message Event
{
"type": "message",
"timestamp": "2025-12-19T10:30:05Z",
"message": "Image reference is valid and accessible"
}
| Field | Type | Description |
|---|---|---|
type |
string | Always "message" |
timestamp |
string | ISO 8601 timestamp (UTC) |
message |
string | Informational message |
Warning Event
{
"type": "warning",
"timestamp": "2025-12-19T10:30:15Z",
"message": "Could not get image digest: network timeout"
}
| Field | Type | Description |
|---|---|---|
type |
string | Always "warning" |
timestamp |
string | ISO 8601 timestamp (UTC) |
message |
string | Warning description |
Error Event
{
"type": "error",
"timestamp": "2025-12-19T10:30:20Z",
"message": "Installation failed",
"details": {
"error": "failed to create partitions: device is busy"
}
}
| Field | Type | Description |
|---|---|---|
type |
string | Always "error" |
timestamp |
string | ISO 8601 timestamp (UTC) |
message |
string | Error summary |
details |
object | Additional error details |
Complete Event
{
"type": "complete",
"timestamp": "2025-12-19T10:31:20Z",
"message": "Installation complete! You can now boot from this disk.",
"details": {
"image": "myimage:latest",
"device": "/dev/sda",
"filesystem": "ext4"
}
}
| Field | Type | Description |
|---|---|---|
type |
string | Always "complete" |
timestamp |
string | ISO 8601 timestamp (UTC) |
message |
string | Completion message |
details |
object | Operation-specific result data |
Consuming JSON Output
Shell Script Example
#!/bin/bash
nbc install --image "$IMAGE" --device "$DEVICE" --json | while IFS= read -r line; do
type=$(echo "$line" | jq -r '.type')
case "$type" in
step)
step=$(echo "$line" | jq -r '.step')
total=$(echo "$line" | jq -r '.total_steps')
name=$(echo "$line" | jq -r '.step_name')
echo "[$step/$total] $name"
;;
message)
echo " $(echo "$line" | jq -r '.message')"
;;
warning)
echo "WARNING: $(echo "$line" | jq -r '.message')" >&2
;;
error)
echo "ERROR: $(echo "$line" | jq -r '.message')" >&2
exit 1
;;
complete)
echo "SUCCESS: $(echo "$line" | jq -r '.message')"
;;
esac
done
Python Example
#!/usr/bin/env python3
import subprocess
import json
import sys
def run_nbc_install(image: str, device: str):
proc = subprocess.Popen(
["nbc", "install", "--image", image, "--device", device, "--json"],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True
)
for line in proc.stdout:
event = json.loads(line.strip())
event_type = event.get("type")
if event_type == "step":
step = event["step"]
total = event["total_steps"]
name = event["step_name"]
print(f"[{step}/{total}] {name}")
elif event_type == "progress":
percent = event.get("percent", 0)
message = event.get("message", "")
print(f" {percent}% - {message}")
elif event_type == "message":
print(f" {event['message']}")
elif event_type == "warning":
print(f"WARNING: {event['message']}", file=sys.stderr)
elif event_type == "error":
print(f"ERROR: {event['message']}", file=sys.stderr)
details = event.get("details", {})
if "error" in details:
print(f" Details: {details['error']}", file=sys.stderr)
return False
elif event_type == "complete":
print(f"\nSUCCESS: {event['message']}")
details = event.get("details", {})
return True
proc.wait()
return proc.returncode == 0
if __name__ == "__main__":
success = run_nbc_install("myimage:latest", "/dev/sda")
sys.exit(0 if success else 1)
Go Example
package main
import (
"bufio"
"encoding/json"
"fmt"
"os/exec"
)
type ProgressEvent struct {
Type string `json:"type"`
Timestamp string `json:"timestamp"`
Step int `json:"step,omitempty"`
TotalSteps int `json:"total_steps,omitempty"`
StepName string `json:"step_name,omitempty"`
Message string `json:"message,omitempty"`
Percent int `json:"percent,omitempty"`
Details map[string]interface{} `json:"details,omitempty"`
}
func runNbcInstall(image, device string) error {
cmd := exec.Command("nbc", "install", "--image", image, "--device", device, "--json")
stdout, err := cmd.StdoutPipe()
if err != nil {
return err
}
if err := cmd.Start(); err != nil {
return err
}
scanner := bufio.NewScanner(stdout)
for scanner.Scan() {
var event ProgressEvent
if err := json.Unmarshal(scanner.Bytes(), &event); err != nil {
continue
}
switch event.Type {
case "step":
fmt.Printf("[%d/%d] %s\n", event.Step, event.TotalSteps, event.StepName)
case "message":
fmt.Printf(" %s\n", event.Message)
case "warning":
fmt.Printf("WARNING: %s\n", event.Message)
case "error":
return fmt.Errorf("%s", event.Message)
case "complete":
fmt.Printf("\nSUCCESS: %s\n", event.Message)
}
}
return cmd.Wait()
}
Command-Specific Output
nbc update --check --json
The --check flag outputs a single JSON object (not streaming):
{
"update_needed": true,
"image": "myimage:latest",
"device": "/dev/sda",
"current_digest": "sha256:abc123...",
"new_digest": "sha256:def456...",
"message": "Update available"
}
nbc status --json
{
"image": "myimage:latest",
"digest": "sha256:abc123...",
"device": "/dev/sda",
"active_root": "/dev/sda3",
"active_slot": "A (root1)",
"bootloader_type": "grub2",
"filesystem_type": "ext4",
"install_date": "2025-12-19T10:00:00Z",
"kernel_args": ["console=ttyS0"],
"update_check": {
"available": false,
"remote_digest": "sha256:abc123...",
"current_digest": "sha256:abc123..."
}
}
nbc validate --json
{
"device": "/dev/sda",
"valid": true,
"message": "Device is valid for bootc installation"
}
Or on error:
{
"device": "/dev/sda",
"valid": false,
"error": "device size 8GB is less than minimum required 10GB"
}
Important Notes
-
Interactive Prompts: When using
--json, interactive confirmation prompts are skipped. Use this flag only in automated environments where you've validated the operation is safe. -
Exit Codes: nbc still uses standard exit codes (0 for success, non-zero for errors) even with
--jsonoutput. Check both the exit code and the final event type. -
Line-by-Line Parsing: Each line is a complete, valid JSON object. Parse line-by-line, not as a single JSON array.
-
Timestamps: All timestamps are in UTC and formatted as ISO 8601 (RFC 3339).
-
Buffering: Output is line-buffered. Each event is flushed immediately for real-time progress monitoring.
-
Stderr: Errors are still written to the JSON output on stdout. Stderr may contain additional debug information when using
--verbose.