Installation
Install a first-party Lockwell SDK and point it at a running daemon. Each language ships the same three surfaces (S3, native, admin) with names that match across SDKs, so the guide examples translate directly.
Prerequisites
You need a running lockwelld with two listeners reachable from your app:
| Listener | Carries | Example |
|---|---|---|
| public | the S3 API and the native JSON API (/api/v1/) | http://localhost:9000 |
| admin | the JSON Admin API (/admin/api/v1/) | http://localhost:9001 |
Endpoints come from your deployment. See Deployment to stand one up. Credentials come from Lockwell itself:
- An admin API token (
lwadm_...) is minted offline on the server host withlockwell admin-token create --role owner. Use it for the Admin API and the app kit. - Access keys (an
accessKeyId+secretKey) are minted through the Admin API or the app kit'sprovisionTenant, per tenant. Use them for the S3 and native data planes. See Tenancy and auth.
Node
npm i @kelphect/sdkRequires Node.js 20 or newer (global fetch, crypto, ReadableStream). The package ships both ESM and CommonJS, so both styles work:
import { Client, NativeClient, AdminClient, LockwellKit } from "@kelphect/sdk"; // ESM
const { Client } = require("@kelphect/sdk"); // CJSGitHub Packages The package is published privately as @kelphect/sdk on the GitHub Packages registry. A
one-time auth setup is required. Put a classic GitHub PAT with read:packages in NODE_AUTH_TOKEN and point the @kelphect scope at GitHub Packages in your .npmrc. :::
Edge runtimes
On Cloudflare Workers, Vercel Edge, Bun, or Deno, import from the dedicated edge entry instead of the default barrel:
import { NativeClient, AdminClient, LockwellKit, verifyWebhook } from "@kelphect/sdk/edge";@kelphect/sdk/edge re-exports only the node:*-free surface (the native client, admin client, app kit, verifyWebhook, RetryPolicy, sha256ChecksumBase64), so a bundler produces a bundle with zero node:crypto and no nodejs_compat flag. The S3 Client is deliberately not exported there. Its SigV4 signer is Node-only. See Edge runtimes.
Go
The private module path is github.com/KelpHect/lockwell. Configure GitHub auth for private modules, then pull only the packages you use:
go env -w GOPRIVATE=github.com/KelpHect/*# App kit (composes admin + native). The usual starting point.
go get github.com/KelpHect/lockwell/pkg/[email protected]
# Or pull individual surfaces directly:
go get github.com/KelpHect/lockwell/pkg/[email protected] # JSON Admin API
go get github.com/KelpHect/lockwell/pkg/[email protected] # native JSON data plane
go get github.com/KelpHect/lockwell/pkg/[email protected] # S3 data plane (SigV4)Each package's non-test source imports only the standard library (plus a small errors helper), so you pull a lightweight dependency tree.
import (
"github.com/KelpHect/lockwell/pkg/lockwellkit"
"github.com/KelpHect/lockwell/pkg/lockwelladmin"
"github.com/KelpHect/lockwell/pkg/lockwellnative"
)Java
The coordinate is com.lockwell:lockwell-sdk, published to GitHub Packages (configure the GitHub Packages repository and a token with read:packages in your build, the same as any GitHub-Packages dependency). It requires Java 21+ and has no third-party runtime dependencies (JDK only: java.net.http, javax.crypto, java.util.zip, java.security).
<dependency>
<groupId>com.lockwell</groupId>
<artifactId>lockwell-sdk</artifactId>
<version>0.2.2</version>
</dependency>dependencies {
implementation 'com.lockwell:lockwell-sdk:0.2.2'
}The three surfaces live in distinct packages:
import com.lockwell.sdk.*; // S3 data plane: LockwellClient
import com.lockwell.sdk.nativeapi.*; // native data plane: LockwellNativeClient
import com.lockwell.sdk.admin.*; // Admin API: LockwellAdminClient
import com.lockwell.sdk.kit.*; // app kit: LockwellKitFor Spring Boot, expose a single LockwellClient (or LockwellKit) @Bean. The clients are thread-safe.
Java private Maven setup
com.lockwell:lockwell-sdk is published privately to GitHub Packages for the KelpHect/lockwell repository. Configure the repository in the application build and put credentials in Maven/Gradle settings, not in source.
<repositories>
<repository>
<id>github</id>
<url>https://maven.pkg.github.com/KelpHect/lockwell</url>
</repository>
</repositories><settings>
<servers>
<server>
<id>github</id>
<username>x-access-token</username>
<password>${env.GITHUB_PACKAGES_TOKEN}</password>
</server>
</servers>
</settings>Use a GitHub token with read:packages for developer machines, CI, and on-prem build runners. If the consuming repository is private and separate from KelpHect/lockwell, the token also needs repository access that can read the private package. In GitHub Actions, write settings.xml from secrets and pass the token through the environment; never commit tokens or generated Maven settings.
Pin immutable versions (0.2.2, not a floating range). Maven/GitHub Packages publish the binary jar, POM, sources jar, and repository-generated checksums for each version. Support bundles and offline install media should mirror those exact artifacts, their checksums, the release tag, and the Lockwell supply-chain evidence (make supply-chain: module verification, vulnerability report, and SBOM). The Java SDK itself has no third-party runtime dependency tree for the core, native, admin, or kit clients.
Maven Central is deferred for this adoption gate. Until a future release explicitly adds Maven Central publishing, customer-facing and offline TangibleShift builds should use an internal/customer artifact mirror seeded from the vetted GitHub Packages artifact, not a developer laptop cache.
Java 25 and API compatibility
The SDK is compiled with --release 21 and is tested on Java 25. That means TangibleShift's Java 25 / Spring Boot 4 services can use the plain S3, native, admin, and kit clients directly. The optional com.lockwell.sdk.spring auto-configuration remains an S3-style convenience surface; native/admin/kit Spring Boot 4 auto-configuration is tracked separately from the runtime compatibility contract.
Pin the SDK and Lockwell server/image to the same release line unless a release note says otherwise. The Java SDK wraps the versioned JSON Admin API at /admin/api/v1 and native API at /api/v1; long-lived on-prem installs can compare the server's published OpenAPI documents (/admin/api/v1/openapi.json, /api/v1/openapi.json) with the SDK version they ship. A mismatch should fail the installer or support preflight before tenant provisioning starts.
Client options
Every client takes the same core inputs (an endpoint plus credentials) and a few optional knobs. The optional ones share names across surfaces.
| Option | What it sets | Default |
|---|---|---|
httpClient / fetch | The transport. Inject your own to control timeouts, pooling, and TLS. | A per-client default |
userAgent | The User-Agent header sent on every request. | lockwell-<lang>-<surface>/0.1 |
retry / retry policy | Automatic retry of safe requests on transient failures. | See Retry setup |
requestTimeout (Java) | Per-request timeout on the JDK HttpClient. | None |
Construct an S3 client with a custom transport:
import { Client } from "@kelphect/sdk";
const s3 = new Client({
endpoint: "http://localhost:9000",
accessKeyId: process.env.LOCKWELL_ACCESS_KEY_ID,
secretKey: process.env.LOCKWELL_SECRET_KEY,
userAgent: "acme-billing/1.4",
});import (
"net/http"
"time"
"github.com/KelpHect/lockwell/pkg/lockwellsdk"
)
hc := &http.Client{Timeout: 30 * time.Second}
s3, err := lockwellsdk.New("http://localhost:9000",
lockwellsdk.Credentials{
AccessKeyID: os.Getenv("LOCKWELL_ACCESS_KEY_ID"),
SecretKey: os.Getenv("LOCKWELL_SECRET_KEY"),
},
lockwellsdk.WithHTTPClient(hc),
lockwellsdk.WithUserAgent("acme-billing/1.4"),
)import com.lockwell.sdk.*;
import java.net.http.HttpClient;
import java.time.Duration;
LockwellClient s3 = LockwellClient.builder()
.endpoint("http://localhost:9000")
.credentials(new Credentials(
System.getenv("LOCKWELL_ACCESS_KEY_ID"),
System.getenv("LOCKWELL_SECRET_KEY")))
.httpClient(HttpClient.newHttpClient())
.userAgent("acme-billing/1.4")
.requestTimeout(Duration.ofSeconds(30))
.build();The native and admin clients accept the same httpClient/userAgent options. The native client also takes refreshSkewMs (Node) to control how far ahead of expiry it refreshes the bearer token.
Secrets never render Clients redact the secret (and, for the native client, the live bearer token) from
toString / inspect / JSON. Logging a client object never leaks credentials. :::
Retry setup
The S3 clients retry safe requests on transient failures: a transport error, a 5xx, or a 429. A request is only ever replayed when replay is safe.
GET,HEAD, andDELETEare idempotent, so they retry freely.- A buffered-body
PUT/POSTretries only when it carries an idempotency key, so the server collapses duplicate effects. - Streaming uploads never retry, because the source is already consumed.
Backoff is exponential (100ms base, doubling, 2s cap) with full jitter.
The defaults differ by language, so set the policy explicitly when it matters:
| SDK | S3 client default | Native client |
|---|---|---|
| Go | on (DefaultRetryPolicy, 3 attempts) | mints a token and retries once on 401 |
| Node | off (1 attempt) | mints a token and retries once on 401 |
| Java | off (1 attempt) | on (RetryPolicy.defaults()) plus one 401 token re-mint |
import { Client, RetryPolicy } from "@kelphect/sdk";
const s3 = new Client({
endpoint: "http://localhost:9000",
accessKeyId: process.env.LOCKWELL_ACCESS_KEY_ID,
secretKey: process.env.LOCKWELL_SECRET_KEY,
retry: RetryPolicy.default(), // opt in (3 attempts)
// retry: new RetryPolicy({ maxAttempts: 5, baseBackoffMs: 200 }), // tune it
// retry: RetryPolicy.disabled(), // the default
});import "github.com/KelpHect/lockwell/pkg/lockwellsdk"
s3, err := lockwellsdk.New(endpoint, creds,
lockwellsdk.WithRetryPolicy(lockwellsdk.DefaultRetryPolicy()), // the default
// lockwellsdk.WithRetryPolicy(lockwellsdk.RetryPolicy{MaxAttempts: 5, BaseBackoff: 200 * time.Millisecond, MaxBackoff: 2 * time.Second, Jitter: 1.0}),
// lockwellsdk.WithRetryPolicy(lockwellsdk.DisabledRetryPolicy()),
)import com.lockwell.sdk.*;
import java.time.Duration;
LockwellClient s3 = LockwellClient.builder()
.endpoint(endpoint)
.credentials(creds)
.retryPolicy(RetryPolicy.defaults()) // opt in (3 attempts)
// .retryPolicy(RetryPolicy.of(5, Duration.ofMillis(200), Duration.ofSeconds(2), 1.0)) // tune it
// .retryPolicy(RetryPolicy.disabled()) // the default
.build();For an idempotency-keyed write to be retried, set the idempotency key on the put. See Conditional writes and idempotency and Errors and retries.
The Java native client uses the same RetryPolicy type and enables RetryPolicy.defaults() by default. It retries GET, HEAD, and DELETE automatically, and retries PUT or POST only when the request carries Idempotency-Key. See Java native client.
Verify the install
A minimal native-client round-trip confirms the endpoint and credentials are wired correctly:
import { NativeClient } from "@kelphect/sdk";
const native = new NativeClient({
endpoint: "http://localhost:9000",
accessKeyId: process.env.LOCKWELL_ACCESS_KEY_ID,
secretKey: process.env.LOCKWELL_SECRET_KEY,
});
console.log(await native.listBuckets());nc, err := lockwellnative.New("http://localhost:9000",
os.Getenv("LOCKWELL_ACCESS_KEY_ID"), os.Getenv("LOCKWELL_SECRET_KEY"))
if err != nil {
log.Fatal(err)
}
buckets, err := nc.ListBuckets(context.Background())
if err != nil {
log.Fatal(err)
}
log.Println(buckets)LockwellNativeClient nc = LockwellNativeClient.builder()
.endpoint("http://localhost:9000")
.accessKeyId(System.getenv("LOCKWELL_ACCESS_KEY_ID"))
.secretKey(System.getenv("LOCKWELL_SECRET_KEY"))
.build();
System.out.println(nc.listBuckets());Next steps
- Getting started. The full five-minute walkthrough.
- The three surfaces. Pick the right client.
- Go SDK, Node SDK, Java SDK. Per-language reference.