Security
How the DLH.io SQL Server Agent protects SQL Server passwords, API keys and storage credentials, what it sends over the network, and how to run it with least privilege.
The agent is designed for environments where the database must not be exposed to the internet and where credentials must stay inside your network. This page explains what is stored where, what leaves the host, and the controls you have.
Credentials at rest
| Secret | Where it lives | Protection |
|---|---|---|
| SQL Server password | dlh_sql_server_credentials.sec | Encrypted on the first run. The plaintext value in dlh_sql_server_credentials.yaml is replaced with <encrypted>. |
DLH.io API key (dlh_ref.api_key) | dlh_agent_config.yaml | Encrypted in place on the first run; is_encrypted_api_key: true is set automatically. |
Static storage credentials (dlh_storage, dlh_log_store, dlh_credential__* of type storage) | dlh_agent_config.yaml | Secret values are encrypted in place on the first run; is_encrypted_storage_password: true is set automatically. |
SCM and API tokens (dlh_credential__* of type scm or api) | dlh_agent_config.yaml | Encrypted in place on the first run; is_encrypted_credential: true is set automatically. |
| Platform issued storage credentials | Encrypted bootstrap cache under %PROGRAMDATA%\DLH\dlh-agent-sql-server | Short-lived, refreshed from the platform, never written to your YAML files. Remove with --clear-credentials-cache. |
| Webhook URLs | dlh_agent_config.yaml or an environment variable | Use url_env_var to keep the URL out of the file entirely. |
| SendGrid API key | dlh_agent_config.yaml or DLH_SENDGRID_API_KEY | Prefer the environment variable. |
Encryption key
All encrypted values use a single symmetric key. The agent looks for it in this order:
- The
DLH_AGENT_SECRET_KEYenvironment variable (recommended for RMM and image-based deployments so that the key is never on disk next to the encrypted files). - The
dlh_agent_secret.keyfile in the agent directory, generated automatically on first run if it does not exist.
Treat the key like a password: back it up with the state database, restrict NTFS permissions on the agent directory to administrators and the service account, and do not copy it into support bundles. If the key is lost the agent cannot decrypt stored secrets; on the next interactive run it offers to re-enter the SQL Server credentials and, for the API key and storage secrets, you re-enter the plaintext values in the YAML and they are encrypted again.
Setting the key for the scheduled task
When you set DLH_AGENT_SECRET_KEY it must be visible to the account that runs the scheduled task (the service account), not only to the administrator who installed the agent. Set it as a system environment variable or in the task definition.
Least privilege
- SQL Server: grant the agent login
SELECTon the tables and views it syncs plusVIEW CHANGE TRACKING(CT) or membership in the CDC gating role. The agent never writes to the source database. - Windows: the installer creates a dedicated local account (
svc_dlh_agent_runner) and grants it the Log on as a batch job and Log on as a service rights. The account needs Modify on the agent directory for state, logs and output. - Object storage: platform issued credentials are scoped to your connection's container or bucket. If you supply static credentials, scope the SAS token or IAM policy to the container / prefix the agent writes to (list, read, write, delete are required for Delta and Iceberg maintenance).
Network egress
The agent makes outbound HTTPS calls only. No inbound ports are opened.
| Destination | Purpose | Can it be disabled? |
|---|---|---|
api.datalakehouse.io | License validation and storage credential bootstrap on each run; run start / completion / failure reports | Bootstrap: no, but without it the agent runs in Community mode. Run reports: platform_run_report: false. |
| Amazon S3 or Azure Storage endpoints | Data and log uploads | Only by running in Community mode (local output only). |
hooks.slack.com, Microsoft Teams / Power Automate, your generic webhook host | Alerts | dlh_notifications.enabled: false or remove the webhook entries. |
api.sendgrid.com | Email alerts | Remove the email settings. |
api.github.com, gitlab.com, api.bitbucket.org, api-dbdeux.datalakehouse.io | Lifecycle actions | post_completion_actions.enabled: false. |
| Microsoft download servers | Only when you run --install-driver --auto | Install the ODBC driver yourself instead. |
--diagnose --offline performs every local check without any network access, which is useful on hosts that must be reviewed before egress rules are opened.
What is sent to the DLH.io platform
- Bootstrap: your organization, project and connection GUIDs, the agent version and the API key. The response contains your tier and, for Customer and Trial accounts, short-lived storage credentials.
- Run reports (
platform_run_report): run identifier, start and end time, status, per-table row counts and outcomes (capped at 50 tables per report), error messages and the storage location of the shipped log folder. Row data is never included. - Log shipping to your
dlh_log_store: the run log and a JSON run summary. Logs are scrubbed of passwords, keys, tokens and SAS signatures before upload.
Data handling
- Row data is written only to the storage you configure (or the local
output_pathin Community mode). It is not sent to DLH.io servers. - Column masking (
hash,partial,redact,round) is applied in memory before rows are written, so masked values never reach storage. See Data retrieval. - The audit trail (
produce_audit_trail: true) writes per-table CT/CDC diagnostic files, including raw extracted rows, to the localaudit/folder. Disable it or shortenaudit_trail_retention_dayson hosts where local copies of source data are not acceptable. - The DuckDB state database contains sync pointers, schema metadata and run history. It does not contain row data.
Binary integrity
The agent ships as a single Windows executable with a private runtime folder. Builds are hardened against casual decompilation and the installer preserves your configuration, keys and state across upgrades. Report suspected tampering or a lost installer to DLH.io support.
Support bundles
When you send logs to support:
- Prefer the shipped log folder in your
dlh_log_store(already scrubbed) or run--diagnose --json > diagnose.json, which redacts secrets. - If you attach a local
logs/dlh_agent_*.log, it contains hostnames and table names but no passwords or keys. - Never attach
dlh_agent_secret.key,dlh_sql_server_credentials.secor the state database.