Get started / COBOL
docuconf for COBOL
The configuration record your program already uses is the declaration: an annotated copybook. docuconf-cobol generate writes the contract and a loader program your code CALLs, and docuconf exec checks the environment and files against the contract before the job starts.
- Builds on
- GnuCOBOL copybooks + docuconf exec
- Package
- docuconf-cobol (Go module)
- Requires
- GnuCOBOL 3; Go 1.25 for the tools
- Status
- v0.1 alpha, not yet released · conformance 134 of 134 (loader under docuconf exec)
- Source
- docuconf-cobol · the orders example
Not published yet
main branch. After the first release it will be go install github.com/docuconf/docuconf-cobol/cmd/docuconf-cobol@latest.The code below is the orders service, the example every SDK ships: six variables, one of them a secret, checked the same way in every language. Compare it across languages.
1.Install
Nothing is on a registry yet. Install both tools from source (Go 1.25+), with GnuCOBOL 3 (apt-get install gnucobol3). docuconf exec is not in a docuconf-go release yet, so the CLI is pinned to the commit this SDK is tested against.
go install github.com/docuconf/docuconf-cobol/cmd/docuconf-cobol@main
go install github.com/docuconf/docuconf-go/cmd/docuconf@v0.0.0-20261008010717-a84031e0174b2.Declare your configuration
The record the program already uses. The comment above a field is its description (the first paragraph) and details (the rest), and @ tags add what the PIC clause cannot say. PIC 9(5) is an int from 0 to 99999 that @max 65535 narrows, level-88s make an enum, OCCURS 8 a list of at most 8 items, and @prefix CFG- makes CFG-PORT the variable PORT.
*> Configuration of the ORDERS-BATCH job, read from the
*> environment by the generated loader ORDCFG.
*> @service orders-batch @prefix CFG- @program ORDCFG
01 ORDERS-CONFIG.
*> Port of the Prometheus metrics endpoint
*> @min 1 @max 65535 @default 8080
05 CFG-PORT PIC 9(5).
*> Log verbosity
*> @default info
05 CFG-LOG-LEVEL PIC X(5).
88 LOG-DEBUG VALUE "debug".
88 LOG-INFO VALUE "info".
88 LOG-WARN VALUE "warn".
88 LOG-ERROR VALUE "error".
*> Postgres connection string of the orders database
*> @type url @schemes postgres @secret @required
05 CFG-DATABASE-URL PIC X(200).
*> Origins whose orders the job accepts
*> @min-items 1 @default "http://localhost:3000"
*> @count CFG-ORIGIN-COUNT
05 CFG-ALLOWED-ORIGINS PIC X(64) OCCURS 8 TIMES.
05 CFG-ORIGIN-COUNT PIC 9(2).
*> Time allowed for each database call, in milliseconds
*> @unit ms @min 1s @max 5m @default 30s
05 CFG-REQUEST-TIMEOUT PIC 9(6).
*> Number of workers that share the input
*>
*> Each worker reads its share of the orders file and holds
*> one database connection, so keep this at or below the
*> pool size:
*>
*> - one connection per worker;
*> - plus one for the summary step.
*> @min 1 @max 64 @default 4
05 CFG-WORKER-COUNT PIC 9(2).
*> The orders to summarise, one per line
*> @file orders text @path /data/orders.txt
*> @path-env ORDERS_FILE @required @max-size 1Mi
05 CFG-ORDERS-PATH PIC X(256).3.Load it at boot
The program CALLs the generated loader once, before it reads any configuration. The loader reads each variable with ACCEPT ... FROM ENVIRONMENT, applies defaults, converts the values into the typed fields and checks ranges, enums and what fits. On a problem it prints them all and returns 1.
MAIN.
CALL "ORDCFG" USING ORDERS-CONFIG
IF RETURN-CODE NOT = 0
STOP RUN
END-IF4.Run it, and see an error
Run the example with a valid environment:
cobc -x -o orders-batch ORDERS-BATCH.cbl ORDCFG.cbl && DATABASE_URL=postgres://orders:pw@db:5432/orders ORDERS_FILE=orders.txt docuconf exec -contract contract.cue -- ./orders-batchWith PORT=0 and no DATABASE_URL, the job does not start. docuconf exec checks the environment and the orders file with the Go SDK's contract-first loader, lists every problem with its error code, writes the termination log and exits 1. Without docuconf exec, the loader still catches PORT=0 itself.
$ PORT=0 ORDERS_FILE=orders.txt docuconf exec -contract contract.cue -- ./orders-batch
...
docuconf: 2 configuration problems:
DATABASE_URL: is required but not set (missing_required)
PORT: 0 is below min 1 (out_of_range)5.Test your configuration
test-config.sh tests the contract with docuconf check and the loader with CFGTEST.cbl, each with an explicit environment (env -i), so the result never depends on the shell. CFGTEST CALLs the loader and checks the fields it stored.
# 1. The contract accepts a known-good environment and rejects a bad one.
printf 'DATABASE_URL=postgres://orders:pw@db/orders\nORDERS_FILE=%s\n' "$here/orders.txt" >"$work/good.env"
env -i PATH="$PATH" DOCUCONF_TERMINATION_LOG=- "$docuconf" check -contract contract.cue -env-file "$work/good.env"
if env -i PATH="$PATH" DOCUCONF_TERMINATION_LOG=- WORKER_COUNT=65 \
"$docuconf" check -contract contract.cue -env-file "$work/good.env" 2>"$work/err"; then
echo "test-config: FAIL: WORKER_COUNT=65 was accepted" >&2
exit 1
fi
grep -q "WORKER_COUNT: 65 is above max 64 (out_of_range)" "$work/err" *> WORKER_COUNT=7 is set; every other optional takes its default.
IF CFG-WORKER-COUNT NOT = 7
DISPLAY "FAIL: WORKER_COUNT " CFG-WORKER-COUNT
END-DISPLAY
ADD 1 TO WS-FAILS
END-IF./test-config.sh6.Export the contract
docuconf-cobol generate writes contract.cue and the loader ORDCFG.cbl from the copybook. Commit both: docuconf-cobol generate -check fails in CI when they no longer match it.
docuconf-cobol generate orders-config.cpy// Code generated by docuconf. DO NOT EDIT.
package orders_batch
import "docuconf.dev/contract"
contract.#Contract & {
apiVersion: "docuconf.dev/v1alpha1"
kind: "ConfigContract"
metadata: {
name: "orders-batch"
generator: {
language: "cobol"
sdk: "docuconf-cobol"
version: "0.1.0"
}
}
vars: {
ALLOWED_ORIGINS: {
type: "list"
description: "Origins whose orders the job accepts"
default: ["http://localhost:3000"]
encoding: "csv"
items: "string"
separator: ","
minItems: 1
maxItems: 8
itemMaxLength: 64
}
DATABASE_URL: {
type: "url"
description: "Postgres connection string of the orders database"
required: true
secret: true
maxLength: 200
schemes: ["postgres"]
}
LOG_LEVEL: {
type: "enum"
description: "Log verbosity"
default: "info"
values: ["debug", "info", "warn", "error"]
}
PORT: {
type: "int"
description: "Port of the Prometheus metrics endpoint"
default: 8080
min: 1
max: 65535
}
REQUEST_TIMEOUT: {
type: "duration"
description: "Time allowed for each database call, in milliseconds"
default: "30s"
min: "1s"
max: "5m"
encoding: "go"
}
WORKER_COUNT: {
type: "int"
description: "Number of workers that share the input"
details: "Each worker reads its share of the orders file and holds\none database connection, so keep this at or below the\npool size:\n\n- one connection per worker;\n- plus one for the summary step."
default: 4
min: 1
max: 64
}
}
files: {
orders: {
type: "text"
description: "The orders to summarise, one per line"
required: true
path: "/data/orders.txt"
pathEnv: "ORDERS_FILE"
maxSize: 1048576
}
}
}The platform then checks each environment's values against contract.cue before it deploys, with docuconf vet, the Helm chart or CUE. See How it works.
7.Containers and CronJobs
The image's entrypoint is docuconf exec, so the job never starts with a bad configuration, and kubectl describe pod shows the report. The loader is standard COBOL plus ACCEPT ... FROM ENVIRONMENT, so the same CALL works with IBM Enterprise COBOL; -runtime copy puts the runtime in a shared copy library. See the example's walkthrough.
COPY --from=docuconf /go/bin/docuconf /usr/local/bin/docuconf
COPY --from=build /out/orders-batch /app/orders-batch
COPY contract.cue /etc/docuconf/contract.cue
# No input is baked in: the platform mounts the orders file at
# /data/orders.txt (or sets ORDERS_FILE).
USER 65532:65532
ENTRYPOINT ["docuconf", "exec", "-contract", "/etc/docuconf/contract.cue", "--", "/app/orders-batch"]Next
- The docuconf-cobol README: every type, file input and option.
- The orders example, with its smoke test.
- The same service in every language, side by side.
- What every SDK supports, and this one's known gaps: Patterns, URL schemes, JSON Schemas and file contents are checked by docuconf exec, not by the loader. A PIC X field holds bytes and contract lengths count characters: leave room for multi-byte UTF-8, or restrict values to ASCII. A value loses its trailing spaces.
Every snippet on this page is compiled or run against docuconf-cobol's main branch in CI. Facts checked 2026-10-08.