A C++20 reference client for System Locker's signed, session-based Bedrock authentication protocol.
Bedrock treats every server response as hostile until its Ed25519 signature is verified with the public key already pinned in the application. The library then checks the response's challenge, system ID, protocol version, server time, and identity hash before it can create or advance a session.
The repository is a source-only distribution. There are two ways to integrate the library into your own project:
- Integrate the source directly — recommended. Copy
include/andsrc/into your project and compile them as part of your own build. - Link the prebuilt static package in
static/— the fastest setup when you prefer a ready-made static library for Windows x64.
The library is provided for your convenience above all else. Compiling the implementation directly into your own binary keeps your existing build flags and lets you step through the source in your own build; the prebuilt package is there when you want a quick, known-good link.
SL-HWID is the default device identifier since 1.0.0 — a change from pre-1.0 versions. It is fault tolerant, cross platform (Windows, macOS, Linux), and combines 14 hardware factors by default: any two can fail or change without changing the HWID, and drifted factors are quietly re-absorbed after each successful authentication. The point is to prevent over-fitting to any single machine detail while avoiding over-dependence on the exact hardware configuration.
Existing pre-v2 HWIDs remain recoverable with their stored schema and threshold; they migrate to the current schema after a successful commit..
If this update enables SL-HWID, reset every existing HWID for the system after deploying 1.0.0 and before affected users authenticate. SL-HWID intentionally produces a different opaque identifier from the pre-1.0 identifier, so existing device claims would otherwise be rejected as an HWID mismatch. Use the dashboard's reset-all-HWIDs action (or your existing administrative reset-every-HWID workflow), then let each user authenticate to claim their new identifier.
This reset is required only when you opt into SL-HWID. Keep existing claims when continuing to send a custom identifier or the legacy identifier.
For this C++ library, Config::hwid defaults to "1", which leaves device
locking disabled. To opt in, set Config::hwid to an empty string and retain
the default Config::hwidMode = "sl-hwid". Set Config::hwidMode = "legacy"
to use the pre-1.0 derivation instead. Any non-empty custom Config::hwid
still takes precedence over the mode.
Default storage is shared by all Bedrock applications for the current user,
so they report the same HWID on the same device. A short-lived interprocess
lock serializes enrollment and refresh; a crashed process's marker is
recovered automatically. Configure a different Config::slHwidStore only
when you deliberately need separate device state. Re-enrolling changes the
HWID for every application sharing that storage.
The HWID determination is deliberately best-effort, but it is expected to match runs of the same application, and, in most cases, across any application run on the same device and operating system.
Name additional hard-locked factors with Config::slHwidExtraMandatory (for example
machine_guid).
A hard lock chosen when the shared device state is enrolled cannot be weakened by another application.
- Ed25519 verification happens over the exact response bytes before JSON is parsed.
- Signing keys are supplied locally by the developer; they are never learned from the authentication server.
- A fresh 64-byte cryptographic challenge is generated for every request.
- Successful heartbeat responses rotate the session token.
- Heartbeats are serialized inside the client to prevent accidental concurrency.
- A lost heartbeat response is retried once with the exact token and challenge so Bedrock can return its cached signed result.
- HTTPS certificate validation is mandatory. Optional curl SPKI pinning adds a second transport-layer check.
- License keys and passwords are not retained for heartbeats or persisted by the library.
- Requested variables and Invisible Folder tokens are read only after the signed Bedrock response verifies.
No client-side library can stop an attacker who fully controls the process from patching or replacing application code. Static linking, code signing, platform hardening, and server-side policy should be used as complementary controls.
Integrating the source directly requires:
- A C++20 compiler (Visual Studio 2019 16.8+ / 2022, GCC 11+, Clang 14+, or AppleClang 14+)
- libcurl
- OpenSSL 1.1.1 or newer (
Cryptocomponent)
OpenSSL is used for Ed25519, secure randomness, and SHA-256. nlohmann/json 3.11.3
is vendored as a private implementation dependency under third_party/.
The prebuilt static SDK additionally requires Windows x64 and a compatible Microsoft Visual C++ toolset. It already contains Bedrock's compiled third-party dependencies. See STATIC-LIBRARY.md.
Dependency notices are included in THIRD_PARTY_NOTICES.md, with complete
license texts alongside the prebuilt SDK.
The repository's static/ directory and the matching release archive provide
a ready-to-link, fully static Bedrock SDK for Windows x64. It already contains
Bedrock, libcurl, OpenSSL Crypto, and zlib compiled with the static /MT
runtime, so no Bedrock or dependency DLL is required.
The package is just headers, libraries, and license texts — it ships no build system files. Link it through your existing build system (for example the Visual Studio steps in STATIC-LIBRARY.md) the same way you would link any other static library.
Use this when you want to compile System Locker Bedrock into your own project instead of linking a prebuilt library.
At minimum, bring these into your project or vendor directory:
include/syslocker/
src/
third_party/nlohmann/json.hpp
Add every .cpp under src/ to your project and keep the private headers from
src/ beside them:
bedrock.cpp
crypto.cpp
curl_http.cpp
invisible_folder.cpp
response.cpp
- Set your project to C++20.
- Add
src/*.cppto your project and keepsrc/crypto.hppbeside them. - Add
include/to C/C++ -> General -> Additional Include Directories. - Add
third_party/nlohmann/to C/C++ -> General -> Additional Include Directories. - Add your libcurl, OpenSSL Crypto, and zlib libraries, plus the Windows SDK libraries, to Linker -> Input -> Additional Dependencies:
libcurl.lib;libcrypto.lib;zs.lib;bcrypt.lib;advapi32.lib;crypt32.lib;secur32.lib;ws2_32.lib;iphlpapi.lib;user32.lib
The exact curl and OpenSSL library names depend on how you provide them (vcpkg, system packages, or a bundled build).
#include <syslocker/bedrock.hpp>The library has no build system files of its own. Integrate it the same way in
any build system: compile src/*.cpp, add include/ and
third_party/nlohmann/ to the include path, and link libcurl, OpenSSL Crypto,
and zlib. Because SYSLOCKER_BEDROCK_SHARED is never defined, the headers use
plain static linkage.
Download the public key from the System Locker dashboard through a trusted, authenticated channel and embed its base64url value in your application.
#include <syslocker/bedrock.hpp>
#include <iostream>
int main()
{
syslocker::bedrock::Config config;
config.systemId = "YOUR_20_CHAR_SYSTEM";
config.version = "1.0.0";
config.hwid = "YOUR-STABLE-HWID";
config.signingPublicKey =
"YOUR_BASE64URL_RAW_ED25519_PUBLIC_KEY";
syslocker::bedrock::Client client(config);
client.onHeartbeatFailure([](const auto &failure)
{
// Stop protected work and exit or return to a safe state.
std::cerr << failure.error.message << '\n';
});
auto auth = client.authenticateWithKey("CUSTOMER-LICENSE-KEY");
if (!auth)
{
// Transport, signature, freshness, or payload-integrity failure.
std::cerr << auth.error().message << '\n';
return 1;
}
if (!auth->sessionStarted)
{
// Authenticated server rejection such as INVALID_KEY.
std::cerr << auth->response.responseCode << ": "
<< auth->response.humanResponse << '\n';
return 2;
}
while (client.isAuthenticated())
{
// Protected application work.
}
}OUTDATED is an authenticated, session-bearing response. Applications should
inspect AuthenticationResult::response.code and apply their own update
policy even when sessionStarted is true.
Ship a replacement public key through your trusted application update channel as part of a coordinated server-key rotation. A session retains the same verified public key for its lifetime, so rotating the server key does not interrupt an already-authenticated client. New processes must be configured with the current public key.
Never download a signing key dynamically from the same unauthenticated server response you are trying to verify.
Set Config::automaticHeartbeats = false to call Client::heartbeatNow() from
your own scheduler. Calling it too early will correctly trigger the server's
terminal timing response.
Request server-side variables and an Invisible Folder Advanced token during
initialization. Missing variables are represented as std::nullopt, matching
Bedrock's signed JSON value of false.
syslocker::bedrock::InitializationOptions options;
options.requestInvisibleFolderToken = true;
options.variables = {"release_channel", "message_of_the_day"};
auto auth = client.authenticateWithKey("CUSTOMER-LICENSE-KEY", options);
if (!auth || !auth->sessionStarted)
return 1;
const auto channel = auth->response.variables.at("release_channel");
if (channel)
std::cout << *channel << '\n';When a running session needs a new Advanced token, request it with a manually scheduled heartbeat. The token stays in memory and is cleared at shutdown.
auto heartbeat = client.heartbeatNow({.requestInvisibleFolderToken = true});
if (!heartbeat)
return 1;Config::invisibleFolderApiKey and the Bedrock token have deliberately
different jobs:
| File permission | Metadata credential | Bedrock helper download |
|---|---|---|
| System Locker Advanced | Short-lived invisible_folder_token / X-Invisiblefolder-Token |
Yes, uses the in-memory Bedrock token |
| API Available | Invisible Folder API key | Metadata only |
| Password Protected | Invisible Folder API key with downloads.password_protected |
Metadata only |
| System Locker Simple | Invisible Folder API key with downloads.system_locker_simple |
Metadata only |
Advanced metadata intentionally rejects an API key, even one with an Advanced download scope. Conversely, API-key metadata cannot use the Bedrock token. The library sends both configured credentials to the metadata endpoint; the server selects the valid one from the file's effective permission.
For updates, downloadIfNew first reads __revisions. Passing a known
revision skips an unchanged file; omit a destination to receive newer bytes in
memory, or provide a destination selected by the application to save them.
auto update = client.invisibleFolder().downloadIfNew(
"release-reference",
"17",
std::filesystem::path{"C:/ProgramData/Example/release.bin"});
if (!update)
return 1;
if (update->downloaded)
std::cout << "Saved revision " << update->revision << '\n';Accounts created through Google sign-in have no local password on the
server. A username/password authentication for such an account is
answered with a signed GOOGLE_SSO_REQUIRED denial whose payload carries
sso_url — the portal where the user completes Google sign-in and receives
a system-specific password (valid 180 days) to use as their account
password. There is no callback; the user transcribes the generated password
into your login form and you simply retry.
const auto auth = client.authenticateWithPassword(username, password);
if (auth && auth->response.code == ResponseCode::GoogleSsoRequired)
{
// The denial's URL is authoritative; open it in the default browser.
std::string portal = auth->response.ssoUrl.value_or(client.googleSsoUrl());
if (!openUrl(portal))
std::cout << "Finish Google sign-in at: " << portal << std::endl; // headless fallback
}You can also start the flow before any denial: client.beginGoogleSso()
(or beginGoogleSso(systemId)) opens the portal and returns an
SsoLaunch{ url, opened }.
include/ stable public API
src/ implementation
examples/ environment-variable based CLI (reference)
third_party/ vendored JSON parser and notice
static/ prebuilt Windows x64 static SDK and release archive
