RedactWall remains pre-release and targets a supervised pilot. These recipes describe source at revision ef77201d9aeee1f0280a204aa9cb3258d6adfdc3. Source availability to authorized repository users does not establish registry publication, a deployed Scan API, public repository access, or parity between language clients. To inspect a source reference below, run its read-only git show command in your authorized skellywix/RedactWall checkout containing this revision. These local references require no off-site navigation.
Go source recipe
Use an authorized RedactWall source checkout at the revision above with Go 1.26.9. The module path is github.com/skellywix/redactwall-v2; import its scanclient package. For a separate application, resolve that module to your local checkout with a go.mod replace directive. This is a local source dependency, not a published module-version recommendation.
Supply a synthetic fixture key, its detector revision and a trusted HTTP client for your local HTTPS fixture. For an admitted deployment, these inputs must instead come from the approved scan_client enrollment. Client and Now are required. Retain certificate and hostname verification in the supplied transport.
Call NewScanClient once when configuring the workload and reuse the returned Publisher for every ScanSynthetic call. Its concurrency limit is per Publisher; constructing another instance creates another independent allowance. The caller retains ownership of the supplied HTTP transport.
package example
import (
"context"
"crypto/ed25519"
"net/http"
"time"
"github.com/skellywix/redactwall-v2/scanclient"
)
func NewScanClient(origin string,
key ed25519.PrivateKey, revision string,
trustedHTTP *http.Client) (*scanclient.Publisher, error) {
return scanclient.New(scanclient.Config{
BaseURL: origin,
SigningKey: key,
DetectorRevision: revision,
Client: trustedHTTP,
Now: time.Now,
RequestTimeout: 5 * time.Second,
MaxConcurrent: 1,
})
}
func ScanSynthetic(ctx context.Context, client *scanclient.Publisher) (scanclient.Result, error) {
return client.Scan(ctx, []byte("Synthetic demonstration content."))
}
Timeout scope: The five-second context starts inside Scan after input validation and admission, before projection and signing. It bounds context-aware HTTP work, but cannot preempt synchronous signing or a caller-supplied transport that ignores context. Client construction and caller work are outside this budget.
Handle the returned error before reading the result. errors.As can extract scanclient.RefusalError (Status and Code); errors.Is can identify scanclient.ErrRefused. An empty Code means the refusal body did not provide a usable code.
- Go client API
git show ef77201d9aeee1f0280a204aa9cb3258d6adfdc3:scanclient/scanclient.go - Go contract tests
git show ef77201d9aeee1f0280a204aa9cb3258d6adfdc3:scanclient/contract_conformance_test.go
Node.js source recipe
With Node 24.19.0, save this ESM module at the authorized RedactWall checkout root so the relative import resolves. For a separate application, the source package consists of package.json, index.mjs and index.d.mts in scanclient/node. Its declared name is @redactwall/scanclient, version 0.0.0, private: true. No npm registry install is implied.
Supply an Ed25519 private KeyObject, the fixture revision and its HTTPS origin. ca is optional PEM trust for that fixture; verification stays enabled. Pass an AbortSignal when the caller needs cancellation. Keep the signing key in the caller process.
Call createScanClient once during workload setup and reuse that client for every scanSynthetic call. The in-flight counter belongs to that instance, so a new client for each call would bypass the intended workload cap.
import { ScanClient } from './scanclient/node/index.mjs';
export function createScanClient({
origin, signingKey, detectorRevision, ca,
}) {
return new ScanClient({
baseURL: origin,
signingKey,
detectorRevision,
requestTimeoutMs: 5000,
maxConcurrent: 1,
ca,
});
}
export async function scanSynthetic(client, { signal } = {}) {
return await client.scan('Synthetic demonstration content.', { signal });
}
Timeout scope: The five-second deadline starts inside scan before content encoding and signing, and is checked again before transmission. Synchronous work cannot be interrupted by its timer. Construction and caller processing are outside this budget; it is not an end-to-end runtime guarantee.
Let a rejected promise stop the caller workflow. ScanError.kind identifies failures; RefusalError additionally carries status and refusalCode. The refusal code may be empty. A successful HTTP response with an invalid result is answer_unusable, not an allow decision.
- Node API and types
git show ef77201d9aeee1f0280a204aa9cb3258d6adfdc3:scanclient/node/index.d.mts - Private source package
git show ef77201d9aeee1f0280a204aa9cb3258d6adfdc3:scanclient/node/package.json - External Node consumer test
git show ef77201d9aeee1f0280a204aa9cb3258d6adfdc3:scanclient/node/test/consumer.test.mjs
Python source recipe
With Python 3.10+ at the authorized checkout revision above, run python scanclient/python/build-archive.py <existing-owned-directory>/redactwall-scanclient.zip from the checkout root. Replace <existing-owned-directory> with an existing directory you own; the output ZIP must not exist. The standalone archive contains only redactwall_scanclient.py and redactwall_https.py and uses the Python standard library. Copy it to your application and replace the path placeholder below with its absolute ZIP path before importing the modules. The build does not install anything or contact a service; this is not a published PyPI package or a complete SDK for other API operations. The separate upstream synthetic TLS fixture also requires Node and OpenSSL.
The caller supplies a raw 32-byte Ed25519 public key and a signer callback returning a raw 64-byte signature over the exact projection bytes it receives. Do not replace the signer with a dummy signature. The source example provides a synthetic signer and ephemeral localhost TLS fixture; a real integration needs an approved key provider.
Call create_scan_transport once and reuse that transport for every scan_synthetic call. Keep using the same instance after a timeout: a worker still blocked in OS DNS retains its capacity until it actually exits. Recreating the transport would discard that shared admission bound.
from datetime import datetime, timezone
import sys
# Replace with the absolute path to your built ZIP, before either SDK import.
sys.path.insert(0, "/absolute/path/to/redactwall-scanclient.zip")
from redactwall_scanclient import prepare_request
from redactwall_https import HTTPSScanTransport
def create_scan_transport(origin, ca_pem=None):
return HTTPSScanTransport(
origin, request_timeout_ms=5000, max_concurrent=1, ca_pem=ca_pem,
)
def scan_synthetic(client, revision, public_key, signer):
request = prepare_request(
b"Synthetic demonstration content.",
detector_revision=revision,
observed_at=datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"),
public_key=public_key,
signer=signer,
)
return client.send(request)
Timeout scope: Request preparation and signing run outside the five-second transport budget, which starts on admission in send. Budget the signer separately. A timed-out DNS worker can outlive the caller deadline while still occupying the shared transport capacity.
Propagate ScanError and TransportError instead of creating a clean result. RefusalError carries status and refusal_code. PreparedRequest.body contains the submitted content: never print or log it. The HTTPS adapter owns its bounded worker/socket cleanup; it does not expose the Node AbortSignal API.
- Standalone Python ZIP build
git show ef77201d9aeee1f0280a204aa9cb3258d6adfdc3:scanclient/python/build-archive.py - External Python ZIP consumer test
git show ef77201d9aeee1f0280a204aa9cb3258d6adfdc3:scanclient/python/test_archive_consumer.py - Python request and result API
git show ef77201d9aeee1f0280a204aa9cb3258d6adfdc3:scanclient/python/redactwall_scanclient.py - Python HTTPS adapter
git show ef77201d9aeee1f0280a204aa9cb3258d6adfdc3:scanclient/python/redactwall_https.py - Runnable synthetic Python fixture
git show ef77201d9aeee1f0280a204aa9cb3258d6adfdc3:scanclient/python/example_synthetic.py
Java source recipe
Use JDK 21 and the authorized checkout. scanclient/java/build-jar.ps1 -OutputPath <existing-directory>/redactwall-scanclient.jar builds the four production classes into a new local JAR; the output file must not exist. Put that JAR on your application classpath. No Maven coordinates or registry publication are established by this build.
Supply a synthetic KeyPair and revision, the fixture HTTPS origin, a caller-resolved InetAddress, and the fixture certificate roots. The transport connects to that address but verifies the origin hostname. DNS resolution belongs to the caller and occurs outside the transport deadline. A null roots argument selects platform trust.
Call createClient once and reuse the transport throughout the workload. Pass a per-call Cancellation to scanSynthetic and retain it if cancellation is needed. In the owning workload's finally block, call closeClient once at shutdown, after all intended scans; do not close the shared client after each scan.
import com.redactwall.scanclient.ScanProtocol;
import com.redactwall.scanclient.ScanTransport;
import java.net.InetAddress;
import java.security.KeyPair;
import java.security.cert.X509Certificate;
import java.time.Duration;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.List;
public final class ScanExample {
public static ScanTransport createClient(
String origin, InetAddress address, List<X509Certificate> roots) throws Exception {
return new ScanTransport(origin, address, roots, Duration.ofSeconds(5), 1);
}
public static ScanProtocol.Result scanSynthetic(
ScanTransport client, KeyPair key, String revision,
ScanTransport.Cancellation cancellation) throws Exception {
var request = ScanProtocol.prepare(
"Synthetic demonstration content.", revision,
Instant.now().truncatedTo(ChronoUnit.SECONDS), key);
return client.start(request, cancellation).await();
}
public static void closeClient(ScanTransport client) throws InterruptedException {
client.close();
if (!client.awaitTermination(Duration.ofSeconds(5))) {
throw new IllegalStateException("Scan transport cleanup incomplete");
}
}
}
Timeout scope: The five-second transport deadline starts in start. Caller DNS resolution, client construction, request preparation and signing occur beforehand. The shutdown wait in closeClient has a separate five-second budget and is not part of the request timeout.
ScanProtocol.ScanException reports preparation/decoding failures. ScanTransport.TransportException exposes kind() and, for refusals, refusal(). At workload shutdown, close the transport and await termination; do not treat an interrupted or incomplete shutdown as a successful integration.
- Java protocol API
git show ef77201d9aeee1f0280a204aa9cb3258d6adfdc3:scanclient/java/src/com/redactwall/scanclient/ScanProtocol.java - Java transport API
git show ef77201d9aeee1f0280a204aa9cb3258d6adfdc3:scanclient/java/src/com/redactwall/scanclient/ScanTransport.java - Local JAR build
git show ef77201d9aeee1f0280a204aa9cb3258d6adfdc3:scanclient/java/build-jar.ps1 - Runnable synthetic Java fixture
git show ef77201d9aeee1f0280a204aa9cb3258d6adfdc3:scanclient/java/examples/com/redactwall/scanclient/SyntheticScanExample.java - External Java consumer
git show ef77201d9aeee1f0280a204aa9cb3258d6adfdc3:scanclient/java/consumer/ExternalConsumer.java
Configure one caller-owned client for the workload and reuse it across calls. Each example selects a five-second timeout and capacity of one for that instance. This is not a five-second wall-clock guarantee for the whole helper, caller processing or shutdown; the language notes above define the scope. Creating a client per call would give each call its own capacity instead of limiting the workload. Input must be nonempty valid UTF-8, at most 128 KiB of content and 256 KiB after request serialization; responses are bounded at 4 KiB. A refusal, malformed answer, timeout or cancellation is not a clean scan. Stop the caller workflow on failure and use an explicit recovery decision. These clients do not automatically retry; a manual repeat is a new scan that may be metered even when the previous call timed out.