Getting Started
SAPL Node is both a PDP server and a CLI tool in a single binary. Applications query it for authorization decisions via HTTP. You use the same binary on your workstation to create, sign, and inspect policy bundles and to generate client credentials. Whether you run the server directly or in Docker, you will need the binary locally for these operations.
Getting the Binary
Download the archive for your platform from the releases page.
On Linux or macOS, extract the archive:
tar xzf sapl-*-linux-amd64.tar.gz
On Windows, extract the .zip file using Explorer or any archive tool.
Each archive contains the sapl binary (ready to run, no runtime dependencies), the LICENSE, and a README.md. Place the binary somewhere on your PATH or in the directory where you plan to run the server.
Verify the installation:
./sapl --version
Quick Start
This walkthrough sets up a working node from scratch, deploys a policy, and queries the PDP. No configuration files are needed. The built-in defaults work out of the box: no TLS, no authentication, policies loaded from the current directory.
Create a working directory and a policy file:
mkdir demo
Create demo/tick.sapl. This policy uses the built-in time PIP to grant access only when the current second is divisible by 5:
policy "tick"
permit
time.secondOf(<time.now>) % 5 == 0
The <time.now> attribute is a stream. It emits the current UTC timestamp once per second. Every time a new value arrives, the PDP re-evaluates the policy and pushes an updated decision to all connected clients.
Start the server:
cd demo && ./sapl
The node starts on localhost:8443 with no TLS and no authentication required. No pdp.json is needed. When absent, the PDP uses the default combining algorithm (PRIORITY_DENY with DENY default and PROPAGATE error handling).
In a separate terminal, request a one-shot decision:
sapl CLI
sapl decide-once --remote -s '"anyone"' -a '"read"' -r '"clock"'
curl
curl -s http://localhost:8443/api/pdp/decide-once -H 'Content-Type: application/json' -d '{"subject":"anyone","action":"read","resource":"clock"}'
The response is a single JSON object. Depending on the current second, the decision is either PERMIT or NOT_APPLICABLE.
Now try streaming. This is where SAPL shows its strength. The PDP holds the connection open and pushes a new decision every time the policy evaluation result changes:
sapl CLI
sapl decide --remote -s '"anyone"' -a '"read"' -r '"clock"'
curl
curl -N http://localhost:8443/api/pdp/decide -H 'Content-Type: application/json' -d '{"subject":"anyone","action":"read","resource":"clock"}'
Watch the output. Every few seconds, the decision flips between PERMIT and NOT_APPLICABLE as the current time crosses a multiple of five. The application does not need to poll. The PDP pushes changes as they happen.
Press Ctrl+C to stop the stream. The PDP cleans up the subscription automatically.
Try editing tick.sapl while the stream is running. Change % 5 to % 10 and save. The PDP detects the change, recompiles the policy, and pushes an updated decision on the same connection. No restart needed.
While the server is running, you can also check its operational state. The health endpoint shows whether policies loaded successfully:
curl -s http://localhost:8443/actuator/health | jq .
You should see "status": "UP" with a pdps detail block showing the state LOADED, the active combining algorithm, and the number of loaded documents. If a policy has a syntax error, the state changes to ERROR and the health status drops to DOWN.
The info endpoint shows PDP configuration (this endpoint requires authentication in production, but works unauthenticated in this setup since allow-no-auth is enabled by default):
curl -s http://localhost:8443/actuator/info | jq .
For Prometheus metrics, Kubernetes probes, and decision logging, see Monitoring.
Directory Layout
The minimal working directory is simply the binary and your policy files:
demo/
sapl
tick.sapl
For more complex setups, add a pdp.json to configure the combining algorithm and a config/application.yml to override defaults:
demo/
sapl
pdp.json (optional)
tick.sapl
config/
application.yml (optional, overrides built-in defaults)
Spring Boot automatically loads config/application.yml on startup. The config-path and policies-path properties default to . (the working directory). For bundle based deployments, the working directory holds .saplbundle files instead of raw .sapl files. See Policy Sources for the different source types and Configuration for the full property reference.
Installing with DEB or RPM
Download the package for your distribution from the releases page.
sudo dpkg -i sapl_4.0.0_amd64.deb
Or for RPM-based distributions:
sudo rpm -i sapl-4.0.0.x86_64.rpm
What the Package Installs
The package creates a sapl system user (no shell, no login) and installs the following files:
/usr/bin/sapl binary
/usr/share/man/man1/sapl*.1 man pages
/usr/share/bash-completion/completions/sapl tab completion
/usr/lib/systemd/system/sapl.service systemd unit
/etc/sapl/application.yml configuration
/var/lib/sapl/ data directory (sapl:sapl, 0750)
/var/lib/sapl/README quickstart guide
/var/lib/sapl/example/ example policies and pdp.json
The configuration file at /etc/sapl/application.yml is preserved on package upgrades. The service unit explicitly loads this file with --spring.config.location=file:/etc/sapl/application.yml, which replaces the JAR-embedded defaults entirely.
The service is configured in BUNDLES mode with signature verification enabled. The node will not start serving decisions until bundle security is configured.
Deploying Your First Bundle
Use the included example policies to create a signed bundle:
sudo sapl bundle keygen -o /etc/sapl/signing
sudo sapl bundle create -i /var/lib/sapl/example -o /var/lib/sapl/default.saplbundle -k /etc/sapl/signing.pem
Configure the public key in /etc/sapl/application.yml:
io.sapl.pdp.embedded:
bundle-security:
public-key-path: /etc/sapl/signing.pub
Managing the Service
Start the service and enable it on boot:
sudo systemctl enable --now sapl
Other common operations:
sudo systemctl stop sapl
sudo systemctl restart sapl
systemctl status sapl
Inspecting Logs
The service logs to the journal. View logs with journalctl:
journalctl -u sapl -f
journalctl -u sapl --since today
journalctl -u sapl -p err
Enable evaluation diagnostics by setting print-text-report: true in /etc/sapl/application.yml and restarting the service. See Monitoring for all diagnostic options.
Verifying
curl -s http://localhost:8443/actuator/health | jq .
You should see "status": "UP". The PDP watches /var/lib/sapl/ for bundle changes and reloads automatically.
Replace the example policies with your own by creating .sapl files in a directory and rebuilding the bundle. See /var/lib/sapl/README for the full workflow.
Service Hardening
The systemd unit runs with strict security restrictions: NoNewPrivileges, ProtectSystem=strict, ProtectHome=true, and write access limited to /var/lib/sapl/. The service cannot write outside its data directory. Place signing keys and TLS keystores in /etc/sapl/ (readable but not writable by the service).
Running with Docker
For container deployments, the server runs inside Docker while you use the local sapl binary for CLI operations like bundle creation and credential generation.
The container image is ghcr.io/heutelbeck/sapl-node. Released versions use the version tag (e.g., ghcr.io/heutelbeck/sapl-node:4.0.0). The examples below use the current development tag 4.0.0-SNAPSHOT. The Docker image defaults to BUNDLES mode with signature verification enabled. The node will not start until bundle security is configured.
To get started with signed bundles:
mkdir policies
echo '{"configurationId":"v1","algorithm":{"votingMode":"PRIORITY_DENY","defaultDecision":"DENY","errorHandling":"PROPAGATE"}}' > policies/pdp.json
echo 'policy "allow-all" permit' > policies/allow-all.sapl
sapl bundle keygen -o signing
sapl bundle create -i ./policies -o ./bundles/default.saplbundle -k signing.pem
Run the container, mounting the bundles directory and the public key:
docker run -p 8443:8443 -v ./bundles:/pdp/data:ro -v ./signing.pub:/pdp/signing.pub:ro -e SERVER_ADDRESS=0.0.0.0 -e IO_SAPL_PDP_EMBEDDED_BUNDLESECURITY_PUBLICKEYPATH=/pdp/signing.pub ghcr.io/heutelbeck/sapl-node:4.0.0-SNAPSHOT
For development or evaluation without signing, disable signature verification:
docker run -p 8443:8443 -v ./bundles:/pdp/data:ro -e SERVER_ADDRESS=0.0.0.0 -e IO_SAPL_PDP_EMBEDDED_BUNDLESECURITY_ALLOWUNSIGNED=true ghcr.io/heutelbeck/sapl-node:4.0.0-SNAPSHOT
To use raw .sapl files instead of bundles (for learning or demos), override the policy source type:
docker run -p 8443:8443 -v ./policies:/pdp/data:ro -e SERVER_ADDRESS=0.0.0.0 -e IO_SAPL_PDP_EMBEDDED_PDPCONFIGTYPE=DIRECTORY ghcr.io/heutelbeck/sapl-node:4.0.0-SNAPSHOT
The SERVER_ADDRESS=0.0.0.0 override is required so Docker’s port mapping can reach the server. The default 127.0.0.1 only accepts connections from within the container.
Environment variables follow Spring Boot’s naming convention: dots become underscores, camelCase becomes uppercase. For example, io.sapl.node.allow-basic-auth becomes IO_SAPL_NODE_ALLOWBASICAUTH. See Configuration for all available properties.
You can also mount a full application.yml instead of using individual environment variables:
docker run -p 8443:8443 -v ./config:/pdp/config:ro -v ./bundles:/pdp/data:ro -e SERVER_ADDRESS=0.0.0.0 ghcr.io/heutelbeck/sapl-node:4.0.0-SNAPSHOT
CLI Reference
The sapl binary doubles as a CLI tool. The CLI commands (decide, decide-once, check, test) do not use Spring Boot configuration. They resolve policies through command-line options or automatic discovery from ~/.sapl/.
Policy Discovery
When no --dir or --bundle flag is given, CLI commands look for policies in ~/.sapl/:
- If
.saplfiles are found, the CLI usesDIRECTORYmode. - If
.saplbundlefiles are found, the CLI usesBUNDLESmode and auto-discovers~/.sapl/public-key.pemfor signature verification. - If both
.sapland.saplbundlefiles are present, the CLI exits with an error (ambiguous source). - Use
--no-verifyto skip bundle signature verification during development.
~/.sapl/
pdp.json optional PDP configuration
*.sapl policy files (DIRECTORY mode)
*.saplbundle bundle files (BUNDLES mode)
public-key.pem optional signing key for bundle verification
Remote Mode
All evaluation commands accept --remote to connect to a running SAPL Node instead of evaluating locally. Connection details can be set via flags or environment variables:
| Flag | Environment Variable | Default |
|---|---|---|
--url |
SAPL_URL |
http://localhost:8443 |
--token |
SAPL_BEARER_TOKEN |
|
--basic-auth |
SAPL_BASIC_AUTH |
Flags take precedence over environment variables. For evaluation command details (decide, decide-once, check), see Getting Started. For test, see Testing SAPL Policies.
Bundle and Credential Commands
The following commands run locally without starting the server. Use them to manage bundles and generate credentials for your application.yml.
| Command | Description |
|---|---|
sapl bundle create |
Create a .saplbundle archive from a directory of .sapl files and a pdp.json. Optionally signs in the same step. |
sapl bundle sign |
Sign a bundle with an Ed25519 private key. |
sapl bundle verify |
Verify a signed bundle against a public key. |
sapl bundle inspect |
Display bundle contents, signature status, and policy list. |
sapl bundle keygen |
Generate an Ed25519 keypair for bundle signing. |
sapl generate basic |
Generate Basic Auth credentials and a ready-to-paste YAML block. |
sapl generate apikey |
Generate an API key and a ready-to-paste YAML block. |
Run any command with --help for the full option reference. See also the CLI Reference for the complete man page documentation.
For authentication and TLS setup, see Security. For health checks and metrics, see Monitoring.