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 .sapl files are found, the CLI uses DIRECTORY mode.
  • If .saplbundle files are found, the CLI uses BUNDLES mode and auto-discovers ~/.sapl/public-key.pem for signature verification.
  • If both .sapl and .saplbundle files are present, the CLI exits with an error (ambiguous source).
  • Use --no-verify to 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.