Skip to main content
Version: v5

Configuration Options

Quick reference for all harper-config.yaml top-level sections.

For how to apply configuration (YAML file, environment variables, CLI, Operations API), see Configuration Overview.


http​

Configures the Harper component server (HTTP, REST API, WebSocket). See HTTP Configuration for full details.

http:
port: 9926
securePort: 4443
cors: true
timeout: 120000
mtls: false
logging:
level: info
path: ~/hdb/log/http.log
  • sessionAffinity — Route requests from same client to same worker thread (ip or header name)
  • compressionThreshold — Response size threshold (bytes) for Brotli compression; 0 disables compression; Default: 0
  • cors — Enable CORS; Default: true
  • corsAccessList — Allowed origins for CORS requests. From v5.4.0, an entry such as https://*.app.example.com allows any one label in place of *; see http.corsAccessList
  • corsAccessControlAllowHeaders — Access-Control-Allow-Headers value for OPTIONS preflight
  • headersTimeout — Max wait for complete HTTP headers (ms); Default: 60000
  • maxHeaderSize — Max HTTP header size (bytes); Default: 16394
  • requestQueueLimit — Max estimated request queue time (ms) before 503; Default: 20000
  • keepAliveTimeout — Inactivity before closing keep-alive connection (ms); Default: 30000
  • port — HTTP port; Default: 9926
  • securePort — HTTPS port; requires TLS configuration; Default: null
  • http2 — Enable HTTP/2; Default: false (Added in: v4.5.0)
  • timeout — Request timeout (ms); Default: 120000
  • mtls — Enable mTLS authentication for incoming connections; sub-options: user, required, certificateVerification (see Certificate Verification)
  • logging — HTTP request logging (disabled by default, Added in: v4.6.0); sub-options: level, path, timing, headers, id. See Logging Configuration

threads​

Worker thread pool configuration.

threads:
count: 11
maxHeapMemory: 300
  • count — Number of worker threads; Default: CPU count minus one
  • maxHeapMemory — Heap limit per thread (MB). When unset, each thread's limit is derived from available memory and the number of HTTP worker threads, including isolated applications' threads
  • maxIsolated Added in: v5.3.0 Changed in: v5.4.0 — How many isolated applications may run, each in a dedicated worker thread; Default: 8. Applications with a running dedicated worker keep it, and the rest are admitted in root-config order up to this limit. An isolated application past it is not loaded anywhere. A deploy_component of one that would not get a worker is refused with a 409. An application whose live release failed closed gets no worker and does not count. Before v5.4.0, every isolated application on disk counted, and a deploy of one that boot order would not admit could be accepted and then never start a worker. 0 admits none. Dedicated threads are added to count, not taken from it, so a node runs up to count + maxIsolated HTTP worker threads. When maxHeapMemory is unset, they all count when Harper divides available memory into each thread's default heap limit. Each one lowers that limit for threads started afterward, until it reaches its 512 MB floor, after which more threads add to the total heap the node may use
  • heapSnapshotNearLimit — Write a .heapsnapshot file when a thread nears its heap limit (loadable in Chrome DevTools Memory tab); Default: false. See Worker Thread Debugging
  • debug — Enable Node.js inspector; sub-options: port, startingPort, host, waitForDebugger. See Worker Thread Debugging
  • preload Added in: v5.2.0 — Module, or list of modules, to load via Node's --import before any Harper or application module on each worker thread. Intended for instrumentation and APM agents. Worker threads only (not under Bun).
  • preloadRequire Added in: v5.2.0 — The same, but via Node's --require (CommonJS). Worker threads only (not under Bun).
  • v8Flags Added in: v5.4.0 — V8 flag, or list of V8 flags, applied to the whole Harper process before its first worker thread starts. Each entry must start with --. Takes effect on process start. Not applied under Bun. See Applying V8 flags
  • shutdownTimeout Added in: v5.4.0 — Longest time (ms) Harper spends shutting down after SIGTERM, SIGINT or SIGQUIT before it exits anyway; Default: 8000 (30000 under harper dev). 0 exits as soon as the signal arrives. Takes effect when Harper restarts. See Shutting down on a termination signal

Preloading an instrumentation agent​

An APM or instrumentation agent has to load before the code it instruments. These two keys put a module on each worker thread's startup, ahead of Harper's own modules and your application's.

The two keys differ in how the module is loaded, and that determines what it can do. --import evaluates the module as ESM, which is how an agent installs Node's module loader hooks so modules loaded later by import can be instrumented. --require runs the module's body, which is how an agent's initialization entry starts it.

Installing loader hooks and starting an agent are separate jobs, and which of an agent's entry points does which is specific to that agent. Some ship a single entry that does both; others split them across an --import entry and a --require entry. When they are split, set both keys.

Bare specifiers resolve against the node_modules of your installed components, so an agent can ship as a dependency of a deployed component. Absolute paths also work.

Follow your agent's own documentation for worker-thread setup, and confirm the telemetry you expect actually reaches your collector.

Example: a split-entry agent​

dd-trace is a split-entry agent, which makes it a useful illustration. On dd-trace 6.x, dd-trace/register.js installs the ESM loader hooks but never calls init(), so preload on its own leaves the tracer uninitialized — it still hands out spans with plausible trace ids, but they are no-ops and nothing is exported. dd-trace/init is the entry that calls init(). Setting both keys covers both jobs:

threads:
preloadRequire: dd-trace/init # the entry that calls init()
preload: dd-trace/register.js # ESM loader hooks for automatic instrumentation

dd-trace/initialize.mjs looks like a single-entry shortcut around that pairing, but it is not one on a worker thread: it gates both its init() call and its loader-hook registration behind isMainThread, so under --import on a worker it starts nothing and registers nothing.

An agent that is one of several preloaded modules still needs its own pairing:

threads:
preloadRequire: dd-trace/init # the entry that calls init()
preload:
- dd-trace/register.js
- /opt/instrumentation/agent.mjs

The dd-trace behavior described here was observed on 6.x. Check your agent's current documentation, and confirm your spans arrive at your collector.

Applying V8 flags​

Some V8 flags, such as --optimize-for-size, cannot be passed through NODE_OPTIONS (Node refuses them there) or to a worker thread. v8Flags applies them inside Harper instead: the main thread calls Node's v8.setFlagsFromString() for each entry before the first worker thread starts, so every worker thread runs with them. V8 flags are process-wide, so they also affect the main thread from that point on. The main thread's isolate already exists, though, so flags V8 reads only when an isolate is created (such as heap sizing) do not change it. With count: 0, the main thread serves requests, so those creation-time flags do not affect request handling; flags V8 reads while running still apply.

threads:
v8Flags:
- --optimize-for-size
- --max-semi-space-size=1

The value can also be set from the environment, for example HARPER_SET_CONFIG='{"threads":{"v8Flags":["--optimize-for-size","--max-semi-space-size=1"]}}'.

  • Validation. Surrounding whitespace is trimmed, and an empty entry is ignored. An entry that does not start with -- is refused by set_configuration, and stops Harper at startup with an error naming threads.v8Flags. V8 skips a flag it does not recognize and only prints Error: unrecognized flag to standard error, so check the spelling against your Node version (node --v8-options).
  • Implied flags are not applied. On the command line, some flags turn on others; --optimize-for-size also caps the young-generation semi-space at 1 MB. Applied at runtime, a flag does not do that, so list the implied flags explicitly, as the example does with --max-semi-space-size=1.
  • When it takes effect. Flags are read once per process. A changed value takes effect when the whole process restarts: the restart operation, which restarts only the node that receives it, or a process or container restart. restart_service restarts only worker threads, which keep the process's flags, so it does not apply a change, even with "replicated": true. To roll back, remove the setting from HARPER_SET_CONFIG or HARPER_CONFIG if it came from there, and from harper-config.yaml (for example set_configuration with "threads_v8Flags": null), then restart the process. A THREADS_V8FLAGS environment variable is written into harper-config.yaml at every startup, so unset it as well; unsetting the variable alone leaves the flags in the file.
  • Expert setting. Flags change V8's behavior for the whole process and are not validated beyond the -- prefix. A conflicting combination can stop the process at startup. Measure memory and throughput with your workload before relying on a flag in production.

Shutting down on a termination signal​

Added in: v5.4.0

When the Harper process receives SIGTERM, SIGINT (Ctrl+C) or SIGQUIT, it shuts down in order instead of exiting at once. These are the signals harper stop, docker stop, Kubernetes and systemd send.

  1. Harper stops accepting new connections.
  2. Requests already in flight get a few seconds to finish (see the first note below).
  3. Component scopes close. Their 'close' listeners run once requests in flight have had a few seconds to finish.
  4. The process exits with code 0.

threads.shutdownTimeout bounds this sequence. If it has not finished in time, Harper logs Shutdown did not finish within <n> ms (threads.shutdownTimeout) and exits anyway. A second signal during the shutdown makes Harper exit at once; running harper stop again sends one. Harper exits with code 0 in all three cases, so a 0 exit status does not mean every 'close' listener finished.

threads:
shutdownTimeout: 25000 # Kubernetes' default 30-second grace period, no preStop hook

The environment variable is THREADS_SHUTDOWNTIMEOUT. Like other settings, a changed value takes effect when Harper restarts; harper stop reads the configuration file, so its wait uses the new value right away. 0 restores the behavior from before v5.4.0: Harper exits when the signal arrives, without draining requests or closing scopes.

  • Requests get a few seconds, not the whole timeout. After it stops accepting connections, Harper waits a few seconds for requests in flight (about 2.5 seconds on Node.js, 5 on Bun), however high threads.shutdownTimeout is set. A request still running then may not finish: on Node.js its connection is closed, and its handler can still be running when 'close' listeners start. The rest of the timeout is for component cleanup.
  • Fit it inside your orchestrator's grace period. A container platform or service manager sends SIGTERM, waits for its grace period, then kills the process. After the shutdown above, Harper closes its databases as the process exits, and threads.shutdownTimeout does not limit that step. Set threads.shutdownTimeout a few seconds below the grace period to leave time for it. The default of 8 seconds fits Docker's default. On Kubernetes, the grace period also covers a preStop hook, which runs before SIGTERM, so subtract its time too.
PlatformGrace period settingDefault
Docker (docker stop)--timeout (-t)10 seconds
Docker Composestop_grace_period10 seconds
KubernetesterminationGracePeriodSeconds30 seconds
systemdTimeoutStopSec90 seconds

authentication​

Authentication and session configuration. Added in: v4.1.0; enableSessions added in v4.2.0. See Authentication Configuration.

authentication:
authorizeLocal: true
cacheTTL: 30000
enableSessions: true
operationTokenTimeout: 1d
refreshTokenTimeout: 30d
  • authorizeLocal — Auto-authorize loopback requests as superuser; Default: true. From v5.4.0, these requests run as the built-in local user.
  • cacheTTL — Session cache duration (ms); Default: 30000
  • enableSessions — Cookie-based sessions; Default: true
  • operationTokenTimeout — Access token lifetime; Default: 1d
  • refreshTokenTimeout — Refresh token lifetime; Default: 30d
  • logging — Authentication event logging (Added in: v4.6.0); sub-options: path, level, tag, stdStreams. See Logging Configuration

operationsApi​

Harper Operations API endpoint configuration. See Operations API Overview.

operationsApi:
network:
port: 9925
cors: true
tls:
certificate: ~/hdb/keys/certificate.pem
privateKey: ~/hdb/keys/privateKey.pem
  • componentFile.maxSize — Maximum file size in bytes returned by get_component_file. Requests for files larger than this limit are rejected with HTTP 413. Default: 5242880 (5 MB)
  • network.cors / network.corsAccessList — CORS settings. network.corsAccessList takes the same entries as http.corsAccessList
  • network.domainSocket — Unix socket path for CLI communication; Default: <rootPath>/hdb/operations-server
  • network.headersTimeout / network.keepAliveTimeout / network.timeout — Timeout settings (ms)
  • network.port — Operations API port; Default: 9925
  • network.securePort — HTTPS port; Default: null
  • tls — TLS override for the Operations API; sub-options: certificate, certificateAuthority, privateKey. See tls

tls​

Global TLS configuration for HTTPS and TLS sockets (used by HTTP and MQTT). Can be a single object or an array for SNI. See TLS and Certificate Management.

tls:
certificate: ~/hdb/keys/certificate.pem
certificateAuthority: ~/hdb/keys/ca.pem
privateKey: ~/hdb/keys/privateKey.pem
  • certificate — Path to TLS certificate; Default: <rootPath>/keys/certificate.pem
  • certificateAuthority — Path to CA file; Default: <rootPath>/keys/ca.pem
  • privateKey — Path to private key; Default: <rootPath>/keys/privateKey.pem
  • ciphers — Allowed TLS cipher suites
  • unixDomainSockets Added in: v5.1.0 — true makes each HTTP worker thread also listen on a Unix domain socket under <rootPath>/sockets for each secure port, for a proxy on the same host to forward to. Isolated applications are reachable only this way, so they require it; Default: false

With tls as a single object, put unixDomainSockets: true inside it. With an SNI certificate array, put tls_unixDomainSockets: true at the root of harper-config.yaml, alongside tls; placing unixDomainSockets inside an array entry does not enable the global setting. On v5.3.0, a standalone TLS_UNIXDOMAINSOCKETS=true environment variable does not enable this SNI configuration; use the root YAML setting:

tls_unixDomainSockets: true
tls:
- host: app.example.com
certificate: ~/hdb/keys/app-certificate.pem
privateKey: ~/hdb/keys/app-private-key.pem

mqtt​

MQTT protocol configuration. Added in: v4.2.0. See MQTT Configuration.

mqtt:
network:
port: 1883
securePort: 8883
webSocket: true
requireAuthentication: true
  • network.port — Insecure MQTT port; Default: 1883
  • network.securePort — Secure MQTT port; Default: 8883
  • network.mtls — Enable mTLS for MQTT connections; sub-options: user, required, certificateAuthority, certificateVerification
  • webSocket — Enable MQTT over WebSocket on HTTP port; Default: true
  • requireAuthentication — Require credentials or mTLS; Default: true
  • logging — MQTT event logging (Added in: v4.6.0); sub-options: path, level, tag, stdStreams. See Logging Configuration

logging​

Application logging. Added in: v4.1.0; per-component logging added in v4.6.0. See Logging Configuration.

logging:
level: warn
root: ~/hdb/log
stdStreams: false
auditLog: true
rotation:
interval: 1D
maxSize: 100M
  • level — Log verbosity (trace → debug → info → warn → error → fatal → notify); Default: warn
  • file — Write to file; Default: true
  • root — Log directory; Default: <rootPath>/log
  • path — Explicit log file path (overrides root)
  • stdStreams — Write to stdout/stderr; Default: false
  • console — Include console.* output in the log file; Default: false
  • auditLog — Enable table transaction audit logging; Default: true
  • auditRetention — Audit log retention duration; Default: 3d
  • external — Logging for components using the logger API; sub-options: level, path
  • rotation.enabled / rotation.compress / rotation.interval / rotation.maxSize / rotation.path — Log file rotation (activates when interval or maxSize is set)
  • auditAuthEvents.logFailed / auditAuthEvents.logSuccessful — Log failed/successful authentication events; Default: false

node​

This node's identity within the cluster. See Replication.

node:
hostname: server-one
  • hostname Changed in: v5.3.0 — This node's identity: it becomes the node's TLS certificate common name and the host that replication advertises to peers and dials to reach this node. Must be a bare hostname or IP literal (e.g. server-one, 10.0.0.5, or the unbracketed IPv6 form ::1) — not a URL and not host:port. Harper fails to start if the value carries a scheme, port, path, credentials, query string, or fragment, is a bracketed IPv6 literal ([::1]), or is not a string; the startup error names the offending value and the reason. Earlier versions accepted such values and silently corrupted certificate matching and replication — a node configured as http://host:9926 advertised and dialed a host literally named http. The same requirement applies to replication.hostname. If unset, Harper uses the first valid bare host among replication.hostname, the host in replication.url, the TLS certificate common name, and the Operations API host, falling back to 127.0.0.1.

replication​

Native WebSocket-based replication (Plexus). Added in: v4.4.0. See Replication and Clustering.

replication:
hostname: server-one
url: wss://server-one:9933
databases: '*'
routes:
- wss://server-two:9933
  • hostname Changed in: v5.3.0 — This instance's hostname within the cluster. Subject to the same bare hostname or IP literal requirement as node.hostname — a URL, host:port, or non-string value fails startup. When both are set, node.hostname wins.
  • url — WebSocket URL peers use to connect to this instance
  • databases — Databases to replicate; Default: "*" (all). Each entry supports name, replicateTo, and sharded
  • routes — Peer nodes; URL strings or {hostname, port, startTime, revokedCertificates} objects
  • port — Replication port
  • securePort — Secure replication port; Default: 9933 (changed from 9925 in v4.5.0)
  • threads — Worker threads dedicated to replication, which then own the replication port and every subscription and load no application code; requires port or securePort; see Dedicated Replication Threads; Default: 0 (replication runs on the HTTP worker threads) (Added in: v5.4.0)
  • enableRootCAs — Verify against Node.js Mozilla CA store; Default: true
  • pingInterval — Interval between replication keep-alive pings (ms); Default: 30000
  • pingTimeout — Time without received socket activity before a replication connection is considered dead (ms); Default: twice pingInterval.
  • confirmationTimeout — Timeout in milliseconds for explicit replicatedConfirmation, read when a waiter starts. Default: 900000, or blobTimeout when greater. An explicitly configured value overrides that fallback, including a smaller value. Range: 1–2147483647. (Added in: v5.4.0)
  • blobTimeout — Blob transfer timeout (ms); Default: 120000
  • blobGapReconnectMs — Interval (ms) for the blob-gap watchdog: when a transient blob save failure pins a replication resume cursor, the connection is forced to reconnect on this cadence so the gapped blob is re-streamed and, during a bulk copy, the copy resumes from the last banked cursor. Lower values heal gaps faster at the cost of more reconnects on a link whose faults never heal; Default: the blobTimeout value
  • copyCursorFlushBytes — Bytes of applied bulk-copy data between durable flushes of the copy resume cursor (RocksDB); Default: 67108864
  • copyCursorFlushIntervalMs — Maximum time (ms) between durable flushes of the bulk-copy resume cursor (RocksDB); Default: 5000
  • blobSendDrainTimeout — Maximum time (ms) a worker waits for in-flight replication blob sends to finish before shutting down during a restart, so a rolling restart (e.g., a component deploy reload) doesn't interrupt a transfer in progress. Only sends that are still making progress are waited on; 0 disables draining; Default: 600000
  • failOver — Failover to alternate node if peer unreachable; Default: true
  • shard — Shard ID for traffic routing; see Sharding
  • shards — Complete array of unique shard IDs, integers 0–65535; order does not matter. Required for a declarative database with no installed map; custom-map conversion can omit it. All peers must agree, and the local shard must be included when configured. Changing it never recomputes an installed map; see Complete shard-id set. (Added in: v5.4.0)
  • mtls.certificateVerification — Certificate revocation checking (CRL/OCSP) for replication connections; see Certificate Verification
  • logging — Replication event logging; sub-options: path, level, tag, stdStreams. See Logging Configuration

storage​

Database storage configuration. See Storage Tuning for guidance on tuning these options for production workloads, Database Overview for general database concepts, and Compaction for reclaiming space inside existing files.

storage:
path: ~/hdb/database
caching: true
compression: true
compactOnStart: false
engine: rocksdb
  • engine — The database storage engine to use for new databases. Currently supported engines are rocksdb and lmdb. The default is rocksdb. Existing databases will use the engine that was used when they were created.

  • fullText Added in: v5.4.0 — Full-text search work and native process limits. Sub-options:

    • maxRawHitsPerRequest — Maximum fetched hits per request, including bounded prefix replay when traversal is needed; default 10000.
    • maxResultsPerRequest — Maximum retained offset plus limit. A request must also fit the separate raw-hit budget; default 10000.
    • requestBudgetMilliseconds — Execution budget across native batches, source checks and highlights, capped by the native execution limit; default 5000 ms. Reader acquisition and freshness waiting have separate bounds.
    • runtime.maxResidentIndexes — Maximum resident native indexes; default 128.
    • runtime.maxIndexingThreads — Maximum aggregate indexing-thread reservations across resident native indexes; default 128.
    • runtime.maxSearchThreads — Maximum aggregate search-thread reservations across resident native indexes; default 256.
    • runtime.maxWriterMemoryBytes — Aggregate writer-memory reservations across resident native indexes; default 4294967296 (4 GiB).
    • runtime.maxQueuedBytes — Aggregate queued-write reservations across resident native indexes; default 8589934592 (8 GiB).
    • runtime.maxExpensiveSearches — Maximum concurrent expensive native searches; default 8.

    The runtime budgets apply together and are not hard bounds on total process memory. See Set Configuration for when request and native runtime changes take effect. Use the usual underscore-separated parameter names, such as storage_fullText_maxRawHitsPerRequest. See full-text query execution for costs; see readiness and recovery for failures.

  • writeAsync Engine: LMDB — Disable disk sync on commit for the legacy LMDB storage engine (disables local disk durability guarantees on LMDB). Has no effect on RocksDB, which does not fsync commits; see Storage Tuning — Durability vs. Throughput; Default: false

  • caching — In-memory record caching; Default: true

  • compression — LZ4 record compression; Default: true (enabled by default since v4.3.0). Sub-options: dictionary, threshold

  • compactOnStart — Compact all non-system databases on startup; Default: false (Added in: v4.3.0)

  • compactOnStartKeepBackup — Retain compaction backups; Default: false

  • maxTransactionQueueTime — Per-thread write-commit duration threshold for HTTP 503 backpressure, and the limit on how long a write keeps retrying conflicts with other writes (a separate 503, code TRANSACTION_COMMIT_CONFLICT_TIMEOUT); see Storage Tuning; Default: 45s

  • maxTransactionOpenTime — Milliseconds a transaction may stay open without activity before Harper aborts its uncommitted writes (which only writes renew) or releases its read-only snapshot (which reads renew; RocksDB only, LMDB uses a fixed 30 seconds); see Storage Tuning; Default: 30000

  • noReadAhead — Advise OS against read-ahead; Default: false

  • prefetchWrites — Prefetch before write transactions; Default: true

  • randomAccessFields Added in: v5.1.0 — Encode records as typed random-access structures, so a single field can be read without decoding the whole record. Best for tables whose records have a stable set of fields with stable types; leave off for wide, sparse, or variably typed schemas. Read by each table when its store opens, so a change applies to existing tables on restart, and ignored entirely by tables declaring the @table(randomAccessFields:) directive; Default: false. See Storage Tuning — Record Encoding

  • path — Database files directory; Default: <rootPath>/database

  • backupPath — Directory for managed database backups (created by create_backup), one subdirectory per database; Default: <rootPath>/backup (Added in: v5.2.0)

  • blobPaths — Blob storage directory or directories; Default: <rootPath>/blobs (Added in: v4.5.0)

  • pageSize — Database page size (bytes); Default: OS default

  • reclamation.threshold — Free-space ratio below which reclamation begins evicting from caching tables; Default: 0.4 (Added in: v4.5.0)

  • reclamation.interval — Free-space check interval; Default: 1h

  • reclamation.evictionFactor — Heuristic factor for early eviction under disk pressure; Default: 100000. See Storage Tuning — Reclamation

  • rocks.blockCacheSize — RocksDB shared block cache size in bytes; Default: 25% of constrained memory. See Storage Tuning — RocksDB Memory (Added in: v5.1.0)

  • rocks.writeBufferManagerSize — Process-wide cap (bytes) on RocksDB memtable memory across all databases. 0 disables; Default: one third of blockCacheSize (enabled). See Storage Tuning — RocksDB Memory (Added in: v5.1.0)

  • rocks.writeBufferManagerCostToCache — When enabled, memtable memory and the block cache share a single, unified memory pool. During heavy write bursts, the block cache dynamically shrinks to give memtables more room. Once those memtables flush to disk, the block cache automatically reclaims that memory space. Default: true. Has no effect when writeBufferManagerSize is 0. (Added in: v5.1.0)

  • rocks.writeBufferManagerAllowStall — Stall writes when memtable memory exceeds writeBufferManagerSize (hard cap) instead of allowing brief overshoot with more aggressive flushing (soft cap); Default: false Changed in: v5.3.0 (true in v5.2.8 and earlier). See Storage Tuning (Added in: v5.1.0)

  • rocks.writeBufferManagerFlushPolicy Added in: v5.4.0 — Which memtable is flushed when memtable memory is over writeBufferManagerSize: oldest, largest, or largestAcrossDBs. Has no effect when writeBufferManagerSize is 0. A changed value applies when the worker threads restart, but removing the key restores the default only after a full restart; an invalid value is ignored, the same as removing the key. Default: largestAcrossDBs when writeBufferManagerAllowStall is true, otherwise oldest. See Storage Tuning

  • rocks.arenaBlockSize Added in: v5.4.0 — Size (bytes) of the blocks each column family's memtable allocates from. Every column family that has been written holds at least one block, charged to writeBufferManagerSize even while idle. Invalid values are ignored. Read when a database opens, so a full restart is what reliably applies a change; Default: automatic, writeBufferManagerSize / 2048 between 64 KiB and 1 MiB (RocksDB's 1 MiB when writeBufferManagerSize is 0). See Storage Tuning

  • rocks.transactionLogAbortMarkers Added in: v5.4.0 — Mark the transaction-log entry of a transaction that fails after logging as aborted, so replication, subscriptions, read_audit_log and crash replay skip it, and give up on a write that keeps conflicting instead of retrying it without limit. false keeps the 5.3 behavior. Read once per process: a change takes effect after a restart, not restart_service. Harper 5.3.4 and earlier cannot read the markers. Default: true. See Storage Tuning — RocksDB Transaction Log

  • rocks.transactionLogWriteIntents Added in: v5.4.0 — Make a commit that would conflict with an older, still-open transaction wait for that transaction to finish, usually before writing its transaction-log entry. Shortens the longest waits under heavy write contention, at the cost of more retries and lower throughput when transactions are large or contention is low. No effect when rocks.transactionLogAbortMarkers is false. Read once per process: a change takes effect after a restart, not restart_service. Default: false. See Storage Tuning — RocksDB Transaction Log


databases​

Per-database and per-table file path overrides. Must be set before the database/table is created. See Database Overview.

databases:
myDatabase:
path: /data/myDatabase
auditPath: /data/myDatabase-audit
tables:
myTable:
path: /data/myTable
  • <dbName>.path — Database files directory
  • <dbName>.auditPath — Audit log directory for this database
  • <dbName>.tables.<tableName>.path — Table files directory

analytics​

Analytics aggregation configuration. See Analytics Overview.

analytics:
aggregatePeriod: 60
replicate: false
  • aggregatePeriod — Aggregation interval (seconds); Default: 60 (Added in: v4.5.0)
  • storageInterval — Aggregation cycles between storage-volume measurements (0 disables); Default: 10
  • replicate — Replicate analytics data across cluster; Default: false
  • logging — Per-subsystem logger override for analytics writes. See Logging Configuration
  • fullText Added in: v5.4.0 — Optional full-text telemetry collection and export; defaults to omitted/off. See Full-text analytics.
  • profiling Changed in: v5.4.0 — Harper Pro only. Run the CPU profiler that records cpu-usage; it samples only in short windows and flags the utilization samples it affects. See analytics.profiling; Default: true

analytics.fullText​

Added in: v5.4.0 Engine: RocksDB

Type: object

Default: omitted; collection is disabled

Optionally collect telemetry from already-open full-text indexes. Defaults are implied; add only the settings you want to change. Operator collection and persistence require Harper analytics to be enabled on a writable node. analytics.aggregatePeriod: -1 disables analytics; read-only mode (storage.readOnly, HARPER_READONLY or the --readonly flag) also disables it. To enable full-text collection when those prerequisites hold:

analytics:
fullText:
enabled: true
OptionTypeImplied valueDescription
enabledbooleanfalseEnable scheduled collection and persistent full-text analytics when Harper analytics is enabled and the node is writable. Changes apply without reopening indexes.
intervalMillisecondsnumber60000Per-handle interval, an integer from 1000 through 2147483647. Calls are staggered across handles.
histogramsbooleanfalseRecord optional latency histograms.
snapshotBytesbooleanfalseRequest cached reader-byte observations; enabling does not trigger index publication or reader reload.

Set enabled: false or remove the block to stop operator collection and export. Invalid edits retain the last valid policy. Deleting the configuration file removes the file-based analytics.fullText setting and reapplies HARPER_DEFAULT_CONFIG, HARPER_CONFIG and HARPER_SET_CONFIG overlays to an empty file; collection remains enabled if one of those overlays still requests it. An in-process override is retired if deletion changes its matching configuration value after those overlays are applied; recreating the file does not restore retired overrides. Internal implementation hooks can independently keep collection active without enabling persistence; they are not a public SDK or operator prerequisite. With operator export enabled, persisted fields reflect the union of operator and internal-consumer recording requests. A handle already closing retains its last options until close settles.

The interval controls periodic live sampling. Successful handle closes also collect final counters, so it does not cap total snapshot or export frequency. Qualify reader churn as well as index count and sampling cadence.

See Full-text analytics for fields, cumulative-counter interpretation and collection costs.


localStudio​

Local Harper Studio GUI. See Studio.

localStudio:
enabled: true
  • enabled — Enable local Studio at http://localhost:<port>; Default: false

componentsRoot​

Path to local component files. Added in: v4.2.0 (previously customFunctionsRoot). See Components.

componentsRoot: ~/hdb/components

rootPath​

Root directory for all Harper persistent data, config, logs, and components.

rootPath: /var/lib/harper

agent​

Added in: v5.2.0

Built-in Harper agent — an LLM loop that operates this instance through Harper's own operations, scoped filesystem access, followup scheduling, the V8 inspector, and outbound HTTP. Disabled by default, so it never incurs LLM cost unless you turn it on. Driven through the Agent operations, which also describe the privilege boundary before you enable it.

agent:
enabled: true
model: default
maxTurns: 50
maxTokens: 16384
maxToolResultBytes: 65536
autoApprove: false
allowDestructive: false
user: hdb_agent
  • enabled — Enable the agent component; Default: false
  • provider — Recorded on the session but not yet used to route the model call — only model reaches the provider. Set the provider through the models configuration instead
  • model — Model id override, passed through to the model call; Default: the models generative default
  • maxTurns — Maximum tool-call iterations in a single run; a positive integer, validated from v5.4.0. A bare maxTurns: with no value fails the agent's start like any other invalid value; Default: 50
  • maxTokens Added in: v5.4.0 — Output-token cap sent with every model request the agent makes. A reply cut off at this cap ends the run with status error, not completed, and lastError says it was cut off; raise the cap if replies keep hitting it. A cut-off reply can also mean the session's transcript filled the model's context window, which a higher cap does not fix; start a new session instead. Keep it within the model's own output limit: most providers reject a larger value on every request, and the run then ends error with the provider's message, so lower it for a model with a small output limit (some older models allow 4096 or 8192). Before v5.4.0 the agent sent no cap: Anthropic models, called directly or through Bedrock, then used 4096, and the other backends sent none; Default: 16384
  • maxToolResultBytes Added in: v5.4.0 — Largest tool result, in bytes of JSON, the agent adds to its conversation. A larger result is cut to this size, ending with a note that gives the original size and tells the model how to ask for less. The file-reading tools return pages of at most half this size, measured as JSON, so a page arrives whole unless the file's path alone takes up most of the cap. Lower it for a model with a small context window. It must be an integer from 1024 to 1048576; at startup an invalid value is logged and the default is used. See When a conversation outgrows the model's context window; Default: 65536
  • maxSessionTokens Added in: v5.4.0 — Per-session cap on prompt plus completion tokens, summed over every model call in the session that returns, a reply the agent rejects as cut off or empty included; a positive integer. See Spend caps; Default: none
  • maxCostUsd Changed in: v5.4.0 — Per-session cap in USD, priced from the pricing on the models.generative entry that served each call; a number of 0 or more, and 0 stops every session before its first call. See Spend caps. Before v5.4.0 this setting was stored but never enforced, and defaulted to 5.00; Default: none
  • autoApprove — Run without per-action approval gates; Default: false
  • allowDestructive — Include the tools marked destructive in the agent's toolset: write_file, the inspector's code-evaluation tools, and any operations tool carrying MCP's destructiveHint (drop_table, delete, restart, set_configuration, ...). When false they are removed entirely rather than gated. That hint comes from a curated set in core which does not cover every damaging operation, so this is not a complete safety boundary on its own — see Agent operations; Default: false
  • user — Harper user the agent's operations tools run as; the filesystem, HTTP, schedule, and inspector tools always run at process privilege regardless. If it cannot be resolved and it is not the default, the agent fails closed and runs with no operations tools; Default: hdb_agent, which falls back to a super_user bootstrap identity
  • componentsScope — The agent's components filesystem scope, which it reads and, with allowDestructive, writes; absolute or relative to rootPath. Read at startup only; Default: the full componentsRoot
  • configScope Added in: v5.4.0 — The agent's read-only config filesystem scope: a file or a directory, absolute or relative to rootPath. Read at startup only. See Filesystem scopes; Default: the Harper config file only
  • httpFetch Added in: v5.4.0 — Whether the agent has its http_fetch tool, and which hosts it may reach: true, false, or { allow: [...] }. Read at startup only. See Restricting http_fetch; Default: true
  • systemPromptAppend — Operator text appended to the agent's system prompt

enabled, provider, model, maxTurns, maxTokens, maxToolResultBytes, maxSessionTokens, maxCostUsd, autoApprove, allowDestructive, and systemPromptAppend can also be changed at runtime with set_agent_config, which applies in memory only. enabled is the exception worth knowing: it cannot switch the agent on, because with the agent disabled at startup no agent operation is registered at all. httpFetch, componentsScope, and configScope are read at startup only, and set_agent_config rejects them.

Filesystem scopes​

Changed in: v5.4.0

The agent's read tools (read_file, list_dir, grep_files, and tail_file) take a root naming one of three scopes. write_file, available when allowDestructive is on, takes no root and always writes to components.

ScopeReachesAccess
componentscomponentsRoot, or componentsScope when setread and write
logsthe log directoryread
configthe Harper config file, or configScope when setread

Before v5.4.0, config was the directory holding harper-config.yaml. On a default install that is rootPath itself, so the agent could read keys/, database/, and the rest of the root. It is now the config file alone, and list_dir shows only that file.

To point the agent somewhere else, such as a directory of extra configuration, set configScope to a file or a directory, absolute or relative to rootPath. It replaces the config file as the scope: the config file stays readable only if the new target contains it, as rootPath does. It is read at startup only. A path that names no file or directory at startup leaves the config scope unavailable and logs an error.

agent:
enabled: true
configScope: /etc/harper/extra

Whatever componentsScope and configScope say, every scope refuses:

  • Harper's key directories, <rootPath>/keys (TLS and JWT keys) and <rootPath>/ssh (git deploy keys): nothing in them is read, listed, or written. Paths are compared after resolving symlinks.
  • Key file names, *.pem, *.key, and .jwtPass: not read. list_dir still shows the names, and write_file can still create such a file outside the key directories.
  • Text holding a PEM private key (-----BEGIN ... PRIVATE KEY-----), such as an inline tls.privateKey in the config file: read_file refuses a page that holds one or starts partway through one (it looks back 128 KiB for the BEGIN line, more than any PEM key spans, post-quantum ones included), tail_file refuses lines that hold one or a file that ends partway through one, and grep_files skips the file.

A key with neither a key file name nor PEM armor, such as a .p12 bundle or raw DER, is not recognized outside the key directories.

Setting configScope to rootPath also exposes the rest of rootPath, though a file over 256 KiB or not UTF-8, such as a database file or a backup, is refused (see Masked credential values).

Masked credential values​

Changed in: v5.4.0

The config file can hold plaintext credentials: a model apiKey passed through HARPER_SET_CONFIG is written there. Logs hold whatever a component logged. When the agent reads the logs or config scope, the value of every credential-named key comes back as *s:

models:
generative:
default:
apiKey: **************
model: gpt-5
  • Which keys. A key that ends in key, token, cookie, or authorization, or contains secret, password, passwd, passphrase, or credential, and accessKeyId in any spelling. The whole key must match, so settings such as maxTokens and operationTokenTimeout are shown. A value that is exactly an environment reference, such as ${OPENAI_API_KEY}, is shown as written.
  • Which forms. key: value and key=value, with the key quoted or not, anywhere in a line: YAML, JSON (including JSON logged inside a JSON string), Authorization: Bearer ... headers, and ?api_key=... query strings. A quoted value is masked up to its closing quote, even on a later line, and an unquoted one to the end of the line. A YAML value that continues on more-indented lines, such as a long folded value or a | block, is masked on every line, as are the - items under a key with no value of its own. In the config scope each file is also parsed as YAML, so a key written with escapes, such as "api\u004Bey", and a value reached through an alias are masked too. A config file that does not parse shows only its keys, with every value masked. A config file over 256 KiB or not UTF-8 is refused.
  • Positional reads. The mask keeps each value in place (in a config file, one * per byte), so a read_file offset into a value, the later parts of a long line, and the end of a line that tail_file returns are masked too. size and nextOffset still count the file's bytes, so a masked value's length is visible; its content is not.
  • Searches. grep_files searches the masked text, so a pattern that matches only a credential's value finds nothing.

The agent's read_log tool is masked the same way, in every text field of each entry. An entry holding a PEM private key is replaced by a refusal. The tool refuses read_log's filter argument, because filter matches the unmasked log. read_log called through the operations API or an MCP client returns the log unmasked.

The components scope is not masked, so a file the agent reads and writes back is unchanged. A credential that is not the value of a credential-named key, such as a password inside a URL, is not masked.

Agent spend caps​

Added in: v5.4.0

maxSessionTokens and maxCostUsd bound a whole session, across every run in it. Approving a paused action or sending a follow-up prompt starts a new run, but does not reset a cap the way it resets maxTurns. Each assistant message records the usage of the model call that produced it, and get_agent_session reports the session's totals as usage.

A cap is checked when a run starts, before it executes an approved action, and after every model call:

  • At or over a cap, the session makes no further model call and ends error, with lastError naming the cap, for example agent.maxSessionTokens=200000 reached: this session used 201450 tokens. Tool calls the model had just requested are answered with a budget_exceeded result rather than run, so the transcript stays valid if you continue the session. A final answer that crosses the cap is still delivered: the run ends completed, with lastError naming the cap.
  • A cap that cannot be measured stops the session too. maxCostUsd needs every call priced, and maxSessionTokens needs every call's token counts. A model with no pricing, or a provider that reports no usage, stops the session after its first call rather than letting it run uncapped: it ends error, or completed if that call was the final answer, with lastError saying why either way. The unmeasured call stays on the session, so the session's total stays unmeasured and later runs stop before making any call, and raising the cap or adding pricing does not clear it: start a new session, or clear the cap. set_agent_config with null clears it until the next restart; to clear it for good, remove the key from the configuration file. A session created before v5.4.0, whose earlier calls recorded no usage, is in the same state.
  • Pending approvals are closed too. A run stopped before it executes an approved action answers that action, and any approval still awaiting a decision, with budget_exceeded, so nothing is left pending. The action does not run later, even once the cap is raised; the model sees the refusal and can ask again.
  • A capped session stays capped. Raise the cap with set_agent_config or start a new session.

A cap is a threshold checked after each call, not a guarantee: the call that reaches it can overshoot it. Every call that returns is counted, including a reply the agent rejects as cut off or empty: that reply stays out of the transcript, but its usage is kept on the session. A call that fails, for example on a timeout or a provider error and whether or not another model then serves it, is not counted, even if the provider billed it. The caps bound one session only: there is no cross-session or per-user budget, so a caller allowed to run agent_prompt can keep spending by starting new sessions.

A cap, or a maxTurns, that is not a usable number fails the agent's start: Harper logs the error and registers no agent operation. Through set_agent_config the same value is rejected with a 400. A bare maxSessionTokens: or maxCostUsd: with no value leaves that cap unset, as null does through set_agent_config; a bare maxTurns: fails, because maxTurns cannot be unset.

If you upgrade with maxCostUsd already set in your configuration, it starts being enforced, and needs pricing on the generative entry the agent uses.

Restricting http_fetch​

Added in: v5.4.0

http_fetch lets the agent send HTTP requests from the Harper process: to its own REST endpoints while it builds an app, or to pull a reference page. The same agent reads table rows, logs, and component source, and text in any of them can steer it, so an enabled agent has a read path and an egress path in one toolset. agent.httpFetch bounds that tool. It is not a bound on every way the agent can reach the network: with allowDestructive on, for example, the agent can write component code, and the Harper process runs that code.

Remove the tool when the agent has no reason to reach the network, for example an investigation or operations-assistant role:

agent:
enabled: true
httpFetch: false

Or keep it and allow only named hosts:

agent:
enabled: true
httpFetch:
allow:
- localhost:9926
- '*.example.com'
ValueEffect
true (default)The tool is available. Cloud-metadata hosts and the IPv4 link-local range are refused.
falseThe tool is removed from the agent's toolset. The model never sees it, and the system prompt stops suggesting HTTP checks.
{ allow: [...] }The tool is available, and a request is refused unless its host matches an entry. Its description lists the entries, so the model knows where it can go.

Allow-list entries take these forms:

  • api.example.com — that host, on any port
  • localhost:9926 — that host on that port only. A URL without a port uses 80 for http and 443 for https.
  • *.example.com — any subdomain, at any depth. It does not match example.com itself; list that separately.
  • [::1] or [::1]:9926 — an IPv6 address, in brackets

Entries and request URLs are compared in canonical form, so letter case, internationalized names, and shorthand spellings of an IPv4 address all match the same entry. An IPv4 entry does not admit the IPv4-mapped IPv6 form of its address ([::ffff:...]); list that separately if you need it.

Some behavior worth knowing:

  • Every redirect hop is checked. A redirect to a host the policy refuses fails the request before that host is contacted. This applies in every mode, so with the default true, a redirect into the cloud-metadata range fails too.
  • The metadata block comes first. Listing a cloud-metadata or link-local address does not make it reachable.
  • Matching is by host name, not address. An allowed name whose DNS resolves to an internal address is still reached. List names whose DNS you control.
  • An empty list removes the tool, the same as false. A value Harper cannot read also removes the tool, and logs an error naming it, while the rest of the agent stays available.
  • It is read at startup only. Set it in harper-config.yaml, with set_configuration, through HARPER_CONFIG, or as an environment variable or CLI argument (AGENT_HTTPFETCH=false, or AGENT_HTTPFETCH_ALLOW='["localhost:9926"]' as a JSON array), then restart. A comma-separated string is not a list: it is rejected, and the tool is removed. set_agent_config rejects it at runtime. Because set_configuration can write it and restart applies it, the policy only holds against the agent itself while those two operations stay out of its toolset (they are not in the default operations allow list) or behind approval.

deployment​

Retention for what deploy_component leaves behind, and how long startup waits for component installs. See deploy_component.

deployment:
startupInstallTimeout: 600000
payloadRetention:
maxSize: 10485760
failedMaxAge: 7d
failedMaxCount: 2
stagingRetention:
maxCount: 5
previews:
sweepInterval: 600000
stagingRetention:
maxCount: 1
  • startupInstallTimeout Added in: v5.3.0 — Milliseconds. The longest startup waits for component installs before it opens listeners. While waiting, it logs every 60 seconds which ones remain outstanding — including throughout 0's unbounded wait — unless every install settles first, which always ends the wait before that first log, whatever the timeout; a failed install logs immediately regardless of the timeout. A component whose install is still running at the deadline keeps installing in the background, and the node serves the version already installed meanwhile, or nothing for a component that was never installed. When that install succeeds, the swap lands on disk immediately and Harper flags a restart as needed (restartRequired in get_status called with no id) — any one component's late success sets it, even while others are still installing. Code already loaded keeps running until that restart, but some component plugins apply an added or changed file on their own rather than waiting for one; content served straight from disk, such as static files, can also reflect the new version before the restart. Outside harper dev, which restarts automatically soon after any restart request, Harper does not restart on its own. An install that fails after the deadline does not set the flag; Harper only logs an error naming the component. 0 waits indefinitely, which was the behavior before v5.3.0. An empty value uses the default. A negative, non-finite, or otherwise unparseable value also falls back to it, with a warning in the log; Default: 600000 (10 minutes)
  • payloadRetention.maxSize Added in: v5.1.15 — Bytes. After a successful deploy, a payload larger than this has its stored tarball (payload_blob) dropped from the hdb_deployment row; the row and its metadata stay. Set it very high to retain every payload; Default: 10485760 (10 MiB)
  • payloadRetention.failedMaxAge Added in: v5.4.0 — A failed deploy keeps its payload, since that is what you retry or debug with. A failed payload older than this is dropped, as delete_deployment_payload drops one, keeping the record; each project's newest failed payload is always kept. A number of seconds, or a number with a unit: m, h, d, M (30 days) or y. Any other value uses the default, and 0 turns the bound off; Default: 7d
  • payloadRetention.failedMaxCount Added in: v5.4.0 — How many failed payloads a project keeps, newest first, counting every node's deploys; older ones are dropped the same way. 0 turns the bound off. Both bounds apply after a deploy fails, for its project, and once at each startup. Each node drops only the payloads of deploys it received, so a deploy received by a node that has since left the cluster keeps its payload; Default: 2
  • stagingRetention.maxCount Added in: v5.3.0 — How many complete builds that are not live may remain per component under <componentsRoot>/.deploy-staging: builds staged with activate: false, and releases replaced by a later deploy, which deployment_id can make live again. The newest survive; older ones are removed when that component next deploys and at startup. drop_component removes all of them. Each is a full installed copy of the component, node_modules included. 0 keeps none, and no replaced release is kept. This bounds dormant builds only, not a disk quota: staging directories that belong to an in-flight or unsettled deploy are never touched; Default: 5
  • previews.maxPerApplication Added in: v5.4.0 — How many live preview deployments of one application a node keeps. A deploy of another is refused with 409, naming the oldest; a redeploy of a live preview does not count. Only the node that receives a deploy checks. A role's component_deploy grant can set its own maxPreviews, which takes the place of this for that role's deploys. A value that is not a positive whole number is ignored; Default: none, no limit
  • previews.sweepInterval Added in: v5.4.0 — Milliseconds. How often the cluster looks for expired previews and drops them. Only a node that holds a preview takes part, and only the scheduler's leader looks. A value that is not a whole number of milliseconds, such as 10m, uses the default; Default: 600000 (10 minutes)
  • previews.stagingRetention.maxCount Added in: v5.4.0 — stagingRetention.maxCount for the component of a preview deployment, in its place. 0 keeps none; Default: 1

applications​

Added in: v5.0.0

Controls how application modules are loaded and isolated. See Module Loading for the full reference.

applications:
lockdown: seal-after-load
moduleLoader: vm-current-context
dependencyLoader: auto
allowedDirectory: app
allowedSpawnCommands:
- npm
- node
# allowedBuiltInModules: [] # if omitted, all Node.js built-ins are allowed
  • lockdown Changed in: v5.4.0 — Whether and when Harper locks the JavaScript intrinsics (see What is locked), which makes prototype pollution attacks harder. The default for new installs is seal-after-load (since v5.4.0), which seals them once all components have loaded, so component initialization can still extend them. Sealing blocks adding or deleting a property, but still allows assigning to an existing writable one. This can also be set to seal (seal before any application code loads; added in v5.4.0), freeze-after-load (the default from v5.0.2 through v5.3; freezes once all components have loaded, so existing properties cannot be changed either), freeze (freeze before any application code loads), ses (lockdown provided by the ses package, which is more strict), or none. An upgrade keeps the value already in harper-config.yaml, and with no value there Harper does not lock the intrinsics. Before downgrading an instance that uses a seal mode to v5.0.1 through v5.3, set freeze-after-load: those releases treat the seal modes as freeze. See Intrinsic Lockdown.
  • moduleLoader — The method used to load modules (and isolate the application). The default is vm-current-context (since v5.1.0; v5.0.x defaulted to vm), which uses Node's VM module loader in Harper's own context so applications share JavaScript intrinsics. This can also be set to vm (VM loader with a separate context and its own intrinsics per application), native (standard Node module loader), or compartment, which uses the ses implementation of the proposed Compartment functionality. See Module Loader Modes.
  • dependencyLoader — The application module loader can be used to load packages/dependencies (installed as dependencies from the package.json). The default is 'auto', which only use the VM module loader if the package specifies harper as a dependency. This can also be set to app to always use the application module loader or native to always native module loader for packages. See Dependency Loading.
  • allowedDirectory — Added in v5.0.4. Restricts where an application may load modules from. The default is app, which permits only modules within the application's own directory tree; loading from outside it throws. Set to any to remove the restriction. Dev-mode installs default to any. Applies to imports the application module loader handles, so treat it as a configuration guardrail rather than a security boundary. See Allowed Directory.
  • allowedBuiltInModules — An allowlist of the Node.js built-in modules applications may import. If omitted, all built-ins are allowed. Matching strips a node: prefix and compares the first path segment, so fs also permits node:fs/promises. Like allowedDirectory, it applies to imports the application module loader handles. See Allowed Built-in Modules.
  • allowedSpawnCommands - This lists the specific commands that can be spawned by the application (using child_process's spawn() and execFile() functions). You can add commands that your application will need to launch (this is to protect against malicious code spawning processes). Only the first token of the command is matched, spawning also requires a mandatory name option, and the call is subject to a node-wide single-process lock — see Child Processes for the full contract.

Component Configuration​

Installed components are configured directly at the root of harper-config.yaml using the component name as the key — not nested under a components: section. See Components.

my-component:
package: 'HarperDB-Add-Ons/my-component'
port: 4321
  • <component-name>.package — NPM package name, GitHub repo (user/repo), or local path
  • <component-name>.port — Port for the component; Default: value of http.port