Skip to content

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:

ListenerCarriesExample
publicthe S3 API and the native JSON API (/api/v1/)http://localhost:9000
adminthe 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 with lockwell 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's provisionTenant, per tenant. Use them for the S3 and native data planes. See Tenancy and auth.

Node

sh
npm i @kelphect/sdk

Requires Node.js 20 or newer (global fetch, crypto, ReadableStream). The package ships both ESM and CommonJS, so both styles work:

js
import { Client, NativeClient, AdminClient, LockwellKit } from "@kelphect/sdk"; // ESM
const { Client } = require("@kelphect/sdk"); // CJS

GitHub Packages During development the package is published privately as @kelphect/sdk on the GitHub Packages

registry (the npm scope must match the GitHub repo owner). 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. It will be republished as @lockwell/sdk on public release. :::

Edge runtimes

On Cloudflare Workers, Vercel Edge, Bun, or Deno, import from the dedicated edge entry instead of the default barrel:

js
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 module path is github.com/lockwell/lockwell (the repository lives at github.com/KelpHect/lockwell). Pull only the packages you use:

sh
# App kit (composes admin + native). The usual starting point.
go get github.com/lockwell/lockwell/pkg/lockwellkit

# Or pull individual surfaces directly:
go get github.com/lockwell/lockwell/pkg/lockwelladmin   # JSON Admin API
go get github.com/lockwell/lockwell/pkg/lockwellnative  # native JSON data plane
go get github.com/lockwell/lockwell/pkg/lockwellsdk     # 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.

go
import (
 "github.com/lockwell/lockwell/pkg/lockwellkit"
 "github.com/lockwell/lockwell/pkg/lockwelladmin"
 "github.com/lockwell/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).

xml
<dependency>
  <groupId>com.lockwell</groupId>
  <artifactId>lockwell-sdk</artifactId>
  <version>0.1.0</version>
</dependency>
groovy
dependencies {
    implementation 'com.lockwell:lockwell-sdk:0.1.0'
}

The three surfaces live in distinct packages:

java
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: LockwellKit

For Spring Boot, expose a single LockwellClient (or LockwellKit) @Bean. The clients are thread-safe.

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.

OptionWhat it setsDefault
httpClient / fetchThe transport. Inject your own to control timeouts, pooling, and TLS.A per-client default
userAgentThe User-Agent header sent on every request.lockwell-<lang>-<surface>/0.1
retry / retry policyAutomatic 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:

ts
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",
});
go
import (
 "net/http"
 "time"

 "github.com/lockwell/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"),
)
java
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, and DELETE are idempotent, so they retry freely.
  • A buffered-body PUT/POST retries 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:

SDKS3 client defaultNative client
Goon (DefaultRetryPolicy, 3 attempts)mints a token and retries once on 401
Nodeoff (1 attempt)mints a token and retries once on 401
Javaoff (1 attempt)mints a token and retries once on 401
ts
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
});
go
import "github.com/lockwell/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()),
)
java
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.

Verify the install

A minimal native-client round-trip confirms the endpoint and credentials are wired correctly:

ts
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());
go
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)
java
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

Released under the Apache-2.0 License. License