Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Threshold
Threshold provides simple and secure access to hosts and services. It is a community-maintained fork of HashiCorp Boundary Community Edition (CE).
About this fork
Threshold is a community fork of HashiCorp Boundary Community Edition. HashiCorp (an IBM company) archived the upstream Boundary CE repository on 2026-10-09 and announced that no further CE releases, maintenance updates, bug fixes or security patches are planned. Threshold continues maintenance of the Community Edition code base so that existing self-managed deployments can keep receiving fixes.
- Threshold is licensed under the Business Source License 1.1, the same license as the upstream code. The original license text and all copyright headers are preserved. See NOTICE for attribution.
- Threshold is not affiliated with, endorsed by, or sponsored by HashiCorp or IBM. "Boundary", "HashiCorp" and "HCP Boundary" are trademarks of their respective owners and are used here only to describe the origin of this project.
- Upstream project: https://github.com/hashicorp/boundary
- This repository: https://repo.anhonesthost.net/CyberCoveLLC/threshold
Compatibility with Boundary CE
This is the first phase of the rebrand and is designed to keep existing Boundary CE deployments working:
- The command-line binary is now
threshold(built fromcmd/threshold). - The wire protocol, HTTP API paths (
/v1), database schema, plugin protocol and configuration file format are unchanged. - The Go module path is
repo.anhonesthost.net/CyberCoveLLC/threshold(with/apiand/sdksubmodules); protobuf code has been regenerated for it. - Release versions are still reported on the wire with the upstream
Boundary vprefix so mixed clusters keep working.Threshold vis also accepted when parsing.
Environment variable and path fallbacks
Threshold prefers THRESHOLD_* environment variables, and transparently falls
back to the legacy BOUNDARY_* name when the THRESHOLD_* variable is not set.
For example THRESHOLD_ADDR, THRESHOLD_TOKEN, THRESHOLD_SCOPE_ID,
THRESHOLD_KEYRING_TYPE, THRESHOLD_TOKEN_NAME, THRESHOLD_CONNECT_*,
THRESHOLD_SKIP_CACHE_DAEMON and the other CLI and server variables are read
first, then BOUNDARY_ADDR, BOUNDARY_TOKEN, and so on. A THRESHOLD_* variable that is
set to a non-empty value always wins. Existing scripts and deployments that
only set BOUNDARY_* variables do not need to change.
Notes:
env://VARreferences inside configuration files and flags (for exampleurl = "env://BOUNDARY_POSTGRES_URL"orurl = "env://THRESHOLD_POSTGRES_URL"as used by the bundled container config) name an environment variable literally and are not rewritten; keep using whichever variable name you export.threshold connect -execexports bothTHRESHOLD_PROXIED_{PORT,IP,ADDR}and the legacyBOUNDARY_PROXIED_{PORT,IP,ADDR}variables to the child process.- The container image entrypoint mirrors any
BOUNDARY_*variables to the matchingTHRESHOLD_*variable when it is unset.
Other fallbacks:
| Item | New | Legacy fallback |
|---|---|---|
| Client cache directory | ~/.threshold |
~/.boundary is used if it exists and ~/.threshold does not |
| Keyring entry (macOS Keychain / Windows Credential Manager) | Threshold Auth Token |
HashiCorp Boundary Auth Token is read if the new entry is not found, and removed on threshold logout |
Keyring prefix (pass, libsecret) |
Threshold |
HashiCorp_Boundary is read if the new entry is not found, and removed on threshold logout |
Tokens are always written using the new names.
Upgrading from Boundary
Threshold is designed as an in-place replacement for HashiCorp Boundary CE:
the same database, the same configuration files, and a binary that can still be
invoked as boundary.
Compatibility guarantees
These are unchanged from upstream Boundary CE, so a mixed fleet of Boundary and Threshold controllers and workers interoperates, and you can upgrade (and roll back) one node at a time:
- Database schema: the schema migrations are untouched and the
boundary_schema_versiontable keeps its name. Threshold runs against an existing Boundary database as-is (threshold database migrateapplies the same migrations Boundary would). - Wire protocol: protobuf packages, gRPC service names and the
/v1HTTP API paths are unchanged (only Go import paths were renamed). Versions are still reported to peers with theBoundary vprefix. - Plugin protocol: the plugin magic cookie
(
HASHICORP_BOUNDARY_PLUGIN/boundary-plugin) is unchanged, so existingboundary-plugin-*binaries keep working. - Configuration file format (HCL) is unchanged.
Do not run database migrate from a newer Threshold than your other nodes
understand; as with Boundary, upgrade controllers before workers and follow the
usual rolling-upgrade guidance for schema migrations.
Linux packages (deb/rpm)
- Back up
/etc/boundary.dand take a database backup. - Install the Threshold package. Do not
purgethe oldboundarypackage (removal is fine); purging runs its scripts, which may delete theboundaryuser that Threshold will reuse. Installing Threshold while the old package is still installed is supported. - The package scripts then, automatically:
- copy
/etc/boundary.dto/etc/threshold.d(boundary.hclbecomesthreshold.hcl,boundary.envbecomesthreshold.env; subdirectories such as TLS material are copied as-is). Existing files in/etc/threshold.dare never overwritten and the originals are never modified or deleted; - keep reading
/etc/boundary.d/boundary.envas a fallback environment file (threshold.envtakes precedence), soBOUNDARY_*variables keep working; - reuse the existing
boundaryservice user and group instead of creating athresholduser (through a managed systemd drop-in,/etc/systemd/system/threshold.service.d/10-legacy-boundary-user.conf), so ownership of your data, log and key directories keeps working; - if
boundary.servicewas enabled and/or running, stop and disable it, enablethreshold.service, and start it if the old service was running. Ifthreshold.servicefails to start, the old service is restored; - make
systemctl restart boundary(andstatus,stop, ...) addressthreshold.servicevia aboundary.servicealias symlink in/etc/systemd/system, and provide/usr/bin/boundaryas a symlink tothresholdif no such file exists. Theboundarycommand prints a one-line deprecation notice to stderr only (silence it withTHRESHOLD_NO_DEPRECATION_NOTICE=1); stdout, flags and exit codes are identical tothreshold.
- copy
- Verify:
systemctl status threshold,threshold version.
What is not migrated automatically:
- Paths inside your configuration files are not rewritten. A config that
refers to
/etc/boundary.d/...or/var/lib/boundarykeeps working because those locations are left in place; update them at your own pace. - A custom
/etc/systemd/system/boundary.serviceunit (a regular file) is left alone and the legacy service is not touched; migrate it by hand. - If
/usr/bin/boundaryis still the old binary from the Boundary package, it is left alone. After removing the old package:ln -s threshold /usr/bin/boundary. - Distribution-specific package-manager behavior such as
--force-confnew(dpkg) can replace/etc/threshold.ddefaults; your originals remain in/etc/boundary.d. - Data directories, the database, KMS keys and the client cache are not moved (the client cache directory is covered by the table above).
Rollback: stop threshold.service, remove the boundary.service alias
symlink (/etc/systemd/system/boundary.service) and the drop-in, reinstall the
old Boundary package, and systemctl enable --now boundary. Because the
database schema is unchanged and /etc/boundary.d was never modified, nothing
else needs to be restored. If you ran a Threshold release that applied schema
migrations newer than your Boundary version, restore the database backup
instead.
Container image
The image's configuration directory moved from /boundary to /threshold
(default command: threshold server -config /threshold/config.hcl). For
compatibility the image:
- contains
/boundaryas a symlink to/threshold, so existing-v "$(pwd)":/boundary/mounts land in the right place; - falls back to
/boundary/config.hcl(printing a deprecation warning) when-config /boundary/config.hclis requested, or when the stock demo config is still in place and a config exists at/boundary/config.hcl; - accepts
boundary ...as the command andBOUNDARY_*environment variables (mirrored toTHRESHOLD_*).
Plan to switch your mounts, -config arguments and environment variables to
the /threshold and THRESHOLD_* names; the compatibility paths print
warnings and may be removed in a future major release.
Other installs (binary / zip)
Replace the boundary binary with threshold (or keep the file name
boundary, which behaves identically). Existing BOUNDARY_* variables,
~/.boundary and stored credentials are picked up as described above.
Development mode
threshold dev creates the ssh.threshold.dev and postgres.threshold.dev
aliases and, for compatibility, the legacy ssh.boundary.dev and
postgres.boundary.dev aliases pointing at the same targets.
Building
make build # builds bin/threshold (requires the embedded UI, see Makefile)
make build-no-ui # builds bin/threshold without the embedded admin UI
The embedded admin UI is built from
CyberCoveLLC/threshold-ui,
a rebranded community fork of HashiCorp's boundary-ui, and is not part of this
repository. internal/ui/VERSION pins the UI commit; make build-ui clones that
commit into internal/ui/.tmp/threshold-ui and builds ui/admin/dist
(requires Node 22+ and pnpm 10). make update-ui-version moves the pin
(UI_COMMITISH=<sha|branch>).
Contributing
See CONTRIBUTING.md.