Barracuda Application Server C/C++ Reference
Native APIs, integration guides, and platform interfaces
SharkTrustX C Client

SharkTrustX C Client is a DNS-only alternative to the Lua ACME client. It is for applications using Barracuda Web Server (BWS), or Barracuda App Server (BAS) with Lua disabled. Public certificates use DNS-01 validation through the SharkTrustX portal.

SharkTrustX gives an embedded web server its own HTTPS certificate and keeps that certificate renewed. Your application supplies a running BWS server, private persistent storage, and portal credentials. The C client registers the device, obtains a certificate, and opens an HTTPS listener for the pages already installed in your server. It uses SharkSSL and native BWS C APIs.

This manual is for C developers who are adding certificate management to a device. You do not need to implement certificate requests or understand the cryptographic message formats. Start by running the native C example, then use the integration steps below for your application. The declarations and short per-function contracts are in SharkTrustX.h.

1. What a certificate does

When someone opens https://controller.example.com, the browser needs to check that it reached the right server. A certificate identifies the server's permitted names, contains its public key, and has a validity period. A certificate authority (CA) signs it. The browser checks the name, dates, and chain of signatures back to a root CA that the browser trusts.

The device keeps the corresponding private key secret. It uses that key during the Transport Layer Security (TLS) connection that protects HTTPS traffic. Copying a certificate alone does not give another device its private key. Copying this client's saved state does, so protect that storage.

Public Key Infrastructure (PKI) is the system of keys, certificates, issuers, and trust settings behind these checks. For this integration, distinguish these three responsibilities:

Component What it does
Your device, running this C client Generates keys, requests and stores certificates, serves HTTPS, and renews before expiry.
SharkTrustX portal or SharkCA portal Enrolls the device and confirms its name and address. The public profile also manages the DNS proof needed for issuance.
Certificate authority Approves the request and signs a certificate. SharkCA provides the private CA in the private profile.

The client speaks ACME (Automatic Certificate Management Environment), the protocol used to request and renew certificates. An ACME directory URL is the service's entry point, from which the client discovers the account and certificate request endpoints.

Choose public or private trust

The same C API has two build profiles. Select one when compiling; there is no runtime switch between them.

SharkTrustX public profile SharkCA private profile
Build selection Leave SHARKTRUSTX_PRIVATE_CA undefined. Define SHARKTRUSTX_PRIVATE_CA consistently in the client/application build.
Issuer Configured public ACME CA. SharkCA at the configured portal.
Certificate identifiers One portal-assigned DNS name. One assigned .local name, optionally with one IPv4 address, or IPv4 only, as approved by the portal.
Proof for issuance DNS-01 through the portal. Authenticated enrollment preauthorizes the approved identifiers and binds them to the ACME account.
Browser trust Requires an issuer trusted by the browser. The shipped example uses a staging issuer, so warnings are expected. The administrator must install the private CA's trust anchor on the computers/browsers that access the device.
Name resolution Portal-managed DNS. Your host supplies multicast DNS (mDNS) advertisement for the assigned .local name. IP-only use needs no mDNS name.

DNS-01 proves control of a DNS name by publishing a temporary TXT record under _acme-challenge.<name>. The CA reads that record from DNS. This does not require the CA to connect to the device's web port. A browser still needs a network path to the device to use its web interface. See the CA's DNS-01 explanation.

Two independent trust settings

The device is both an HTTPS client and an HTTPS server:

Connection Who checks the certificate? Where trust is configured
Device to portal/ACME service SharkSSL on the device checks the remote service. Your outbound SharkSsl* clientSsl, containing the appropriate trusted CA list.
Browser to device The browser checks the device's certificate. The browser/operating system trust store.

Adding a CA to clientSsl does not make browsers trust your device. Likewise, a browser warning about a staging device certificate does not mean the device should disable validation of the portal. The library always requires trusted outbound HTTPS. Both sides also need correct time for date checks.

The example uses the Let's Encrypt staging service. Its issued certificates are for testing and are not trusted by ordinary browsers. Do not add staging roots to your everyday browser trust store.

2. Prepare the host

Before calling the client, provide these host services:

  1. Network and clock. Initialize sockets, DNS resolution, and a correct UTC clock. The device must reach its portal and ACME service over HTTPS.
  2. Randomness. Seed SharkSSL with real platform or hardware entropy before generating credentials or keys. The desktop example uses Windows BCrypt or Linux /dev/urandom. Its RTOS seed is a placeholder that you must replace.
  3. Server. Construct ThreadMutex, SoDisp, and HttpServer, and install your application's pages. Run the dispatcher for the lifetime of the client. Starting the client immediately before SoDisp_run(), as in the example, is supported.
  4. Outbound TLS. Construct a client-mode SharkSsl and load the CA list needed to authenticate your portal and ACME service. Keep its backing data alive according to the SharkSSL API.
  5. Worker. Construct a dedicated BaTimer using the server's dispatcher mutex. Network operations run on this worker.
  6. Storage and provisioning. Supply a private, persistent IoIntf and your portal-generated tokengen C file, as described below.

An IoIntf is BWS's input/output interface. It can represent a disk directory or a device filesystem. The client needs only its file operations; the storage must not be attached to the web server's public content tree.

Only one SharkTrustX client per process is supported. It manages the HTTPS listeners for your device's web interface. Set SharkTrustX_Config.port to 443, the normal HTTPS listening port, or another available port. The example uses 8443. Reserve the selected HTTPS port for SharkTrustX; do not create another HTTPS listener on that same port.

With **USE_IPV6 enabled**, the client first tries an IPv6 listener, then an IPv4 listener on the same HTTPS port, following the Mako/Xedge approach. It keeps whichever listeners open successfully. Without USE_IPV6, it tries only IPv4. The selected BWS network port must implement IPv6 for the IPv6 attempt to succeed; defining the macro alone does not add that support.

With the default wildcard binding (interfaceName = NULL), Windows normally opens both sockets. Linux normally opens one dual-stack IPv6 socket that also accepts IPv4; the subsequent IPv4 bind fails because the port is already covered. Platform settings can change this behavior. The client leaves socket policy unchanged and fails installation only if neither listener opens. A failed IPv4 attempt is not proof that the IPv6 socket accepts IPv4, so verify both families on your target. status.ready means at least one listener is open with an unexpired certificate, not that both families are reachable.

The client creates one server-mode TLS context and installs the device's certificate there. Both listeners share this context and certificate. Your clientSsl is a separate object used for outbound HTTPS connections to the portal and certificate authority.

Portal address registration remains IPv4. A certificate for a DNS name can serve HTTPS over either address family when that name resolves to the device. For SharkCA, your mDNS responder can advertise the assigned .local name with an IPv6 AAAA record when IPv6 HTTPS is available. This does not add an IPv6 address identifier to the certificate: a literal IPv6 URL is not covered by a DNS-name certificate. Public SharkTrustX certificates require the assigned public hostname, not a different .local name; IPv6 access also requires that hostname to resolve to an IPv6 address.

Provision with tokengen

Download the generated **tokengen.c** file from your SharkTrustX portal or SharkCA portal. The portal generates this C source for your provisioning credentials. Compile it as part of your application with TOKENGEN_C_API defined to expose the pure C API. It supplies tokengen_info() and tokengen_proof(), as used in the example:

  • tokengen_info(const char** domain, const uint8_t** key) returns borrowed pointers to the portal domain and 32-byte zone key through its outputs.
  • tokengen_proof(const uint8_t* message, size_t length, uint8_t proof[32]) computes the 32-byte proof used to authenticate portal requests. It returns zero on success and nonzero on failure.

Use the matching domain, key, and proof function from the same generated file. The proof input is binary and can contain zero bytes. Its explicit length is essential; replacing it with strlen() produces an incorrect proof.

Keep generated credential source and resulting firmware/executables private. Tokengen obfuscation protects the compiled provisioning credential only. It does not encrypt the client's saved state or supply random entropy.

3. Start certificate management

The following is a complete client-start helper for an already initialized host, using the public staging profile. Server construction, clock, entropy, CA-list loading, and storage mounting are the host's responsibilities from the previous section. For a complete runnable server, use examples/SharkTrustX/src/start.c and the example build guide.

The helper's four pointer arguments are required, initialized host objects: HttpServer* server, BaTimer* timer, IoIntf* privateStore, and SharkSsl* outboundTls. They must remain valid until successful cleanup. The return type is int: zero means accepted; a negative value is an immediate SharkTrustX_Code rejection. Call the helper once, from a normal application task that does not already hold the server mutex.

#include <SharkTrustX.h>
#include <HttpTrace.h>
#include <stdint.h>
/* Exported by your generated tokengen.c when TOKENGEN_C_API is defined. */
extern void tokengen_info(const char** domain, const uint8_t** key);
extern int tokengen_proof(const uint8_t* message, size_t length,
uint8_t proof[32]);
/* Static storage keeps the borrowed configuration alive after this returns. */
static SharkTrustX_Config certConfig;
static void reportCertificate(void* context, int code)
{
(void)context;
/* The worker already holds the mutex. Keep the trace sink nonblocking. */
if(code == SharkTrustX_Ready)
HttpTrace_printf(0, "HTTPS certificate installed.\n");
else
HttpTrace_printf(0, "Certificate management event: %d\n", code);
}
static void reportName(void* context, const char* name, size_t length)
{
(void)context;
/* Use the confirmed name. The portal may have changed the preferred label. */
if(name)
HttpTrace_printf(0, "Device name: %.*s\n", (int)length, name);
}
int startCertificates(HttpServer* server, BaTimer* timer,
IoIntf* privateStore, SharkSsl* outboundTls)
{
ThreadMutex* mutex = HttpServer_getMutex(server);
int rc;
/* This first-start configuration is zero-initialized by static storage. */
certConfig.server = server;
certConfig.timer = timer;
certConfig.io = privateStore;
certConfig.clientSsl = outboundTls;
tokengen_info(&certConfig.portalDomain, &certConfig.zoneKey);
certConfig.proof = tokengen_proof;
certConfig.name = "controller-17";
certConfig.directoryUrl =
"https://acme-staging-v02.api.letsencrypt.org/directory";
certConfig.email = "john.doe@example.com"; /* Replace with your contact. */
certConfig.acceptTerms = TRUE; /* Set only after accepting the CA's terms. */
certConfig.port = 8443; /* Example HTTPS port; normally use 443. */
certConfig.onEvent = reportCertificate;
certConfig.onName = reportName;
ThreadMutex_set(mutex);
rc = SharkTrustX_start(&certConfig);
ThreadMutex_release(mutex);
return rc; /* Zero means work was scheduled, not that HTTPS is ready. */
}
BA_API void HttpTrace_printf(int prio, const char *fmt,...)
Write data to the trace buffer.
Automatic certificates for one native BWS HTTPS port.
@ SharkTrustX_Ready
Usable certificate installed; renewal scheduled.
Definition: SharkTrustX.h:76
BA_API int SharkTrustX_start(const SharkTrustX_Config *config)
Start registration, certificate installation, and automatic renewal.
Timer manager used for one-shot and periodic callbacks.
Definition: BaTimer.h:67
The Web Server.
Definition: HttpServer.h:3393
The IoIntf class specifies an abstract file API, implementations include ZipIo, DiskIo,...
Definition: IoIntf.h:482
Required integration objects and provisioning information.
Definition: SharkTrustX.h:123
const char * name
Preferred hostname label, 1..63 ASCII letters/digits/hyphens; no leading/trailing hyphen.
Definition: SharkTrustX.h:131
U16 port
Required unused HTTPS port, 1..65535, normally 443.
Definition: SharkTrustX.h:143
BaBool acceptTerms
Must be TRUE publicly; ignored privately.
Definition: SharkTrustX.h:138
SharkTrustX_Name onName
Optional confirmed-name callback, or NULL.
Definition: SharkTrustX.h:146
BaTimer * timer
Required dedicated timer using the server's mutex.
Definition: SharkTrustX.h:125
const char * email
Required contact email in public builds; ignored privately.
Definition: SharkTrustX.h:137
SharkSsl * clientSsl
Required client TLS context with trusted CA list.
Definition: SharkTrustX.h:127
const unsigned char * zoneKey
Required borrowed 32-byte tokengen zone key.
Definition: SharkTrustX.h:129
const char * portalDomain
Required tokengen domain only, without scheme/path.
Definition: SharkTrustX.h:128
SharkTrustX_Proof proof
Required tokengen proof callback.
Definition: SharkTrustX.h:130
HttpServer * server
Required initialized server with running dispatcher.
Definition: SharkTrustX.h:124
SharkTrustX_Event onEvent
Optional status callback, or NULL.
Definition: SharkTrustX.h:145
const char * directoryUrl
Required absolute HTTPS ACME directory in public builds.
Definition: SharkTrustX.h:134
IoIntf * io
Required private writable state store; see Store keys and enrollment safely.
Definition: SharkTrustX.h:126
A mutual exclusion class.
Definition: ThreadLib.h:196

For a fresh store, the sequence is:

  1. SharkTrustX_start() checks configuration, allocates the client, and schedules its worker. A zero return accepts the request.
  2. The worker saves its account key and a pending device credential before enrollment. Retrying a lost registration response reuses that credential.
  3. The portal confirms enrollment and assigns the final name. onName receives it after the confirmation has been saved.
  4. The client requests a certificate, completes authorization, and submits a certificate signing request (CSR) containing its public key and approved names. The private key stays on the device.
  5. The client checks and saves the returned certificate, opens the HTTPS listener, and calls onEvent(context, SharkTrustX_Ready).
  6. The worker continues renewal and retry work automatically.

A confirmed name is not evidence that step 5 has happened. Use SharkTrustX_getStatus() to decide whether HTTPS currently has a usable certificate. Conversely, on restart a saved certificate can be installed and produce Ready before enrollment refresh produces onName. Do not depend on a fixed callback order.

The portal uses its increment name policy. For example, a requested label controller-17 can result in a different assigned name when that label is already taken. Use the full name delivered to onName; do not construct a URL from the preferred label or search for an unused suffix yourself.

4. Configure the client

Initialize SharkTrustX_Config to zero before its first use, then set the required members. There is no default HTTPS listening port, trust list, storage location, or portal identity. Except where stated, strings are NUL-terminated.

Borrowed means the client uses your object or memory without taking ownership. The configuration itself, its strings, and its referenced objects must remain alive and unchanged until successful cleanup completes. A local configuration on the stack of a function that returns is therefore unsuitable.

Member C type Requirement and meaning
server HttpServer* Required initialized server. Its dispatcher must run throughout management.
timer BaTimer* Required dedicated worker timer sharing exactly HttpServer_getMutex(server).
io IoIntf* Required private writable persistent store; see
  1. Store keys and enrollment safely
.
clientSsl SharkSsl* Required client-mode TLS context with CA anchors for all outbound services.
portalDomain const char* Required tokengen domain only, such as portal.example.com. No scheme, port, or path. At most 253 ASCII characters, with valid DNS labels.
zoneKey const unsigned char* Required borrowed pointer to exactly 32 bytes from tokengen; not a hexadecimal text string.
proof SharkTrustX_Proof Required binary proof callback from the same tokengen source.
name const char* Preferred label, 1 to 63 ASCII letters, digits, or hyphens; no leading/trailing hyphen or dot. Required publicly. NULL selects IP-only enrollment privately; an empty string is invalid.
directoryUrl const char* Required publicly: absolute https:// ACME directory URL, at most 767 bytes. No credentials, fragment, whitespace, or backslash. Ignored privately; the client uses https://<portalDomain>/acme/directory.
email const char* Required publicly: nonempty contact address, at most 254 bytes, containing @. Supply the address without mailto:. Ignored privately.
acceptTerms BaBool Must be TRUE publicly, after accepting the CA's terms. Zero initialization gives FALSE, which rejects a public start. Ignored privately.
interfaceName const char* Optional local bind-address string passed to both listener attempts. NULL selects wildcard binding for both families. A literal IPv4 or IPv6 address restricts binding to that family; it does not identify a corresponding address in the other family. This is an address, not an adapter name such as eth0. It does not select the address published to the portal.
port U16 Required device HTTPS listening port, 1 to 65535; normally 443. The example uses 8443. The port must be available; there is no implicit default.
onEvent SharkTrustX_Event Optional lifecycle/error callback; NULL suppresses these notifications. Synchronous errors still return normally.
onName SharkTrustX_Name Optional assigned-name callback, independent of onEvent; default NULL.
context void* Optional application pointer passed unchanged to both callbacks; default NULL.

Adapt the configuration for SharkCA

Compile the private profile and use credentials from your SharkCA portal. Keep the same server, timer, storage, and callback setup. Before the initial SharkTrustX_start(), use the following alternative field assignments in the helper above:

/* Alternative for a SHARKTRUSTX_PRIVATE_CA build, before its first start. */
certConfig.name = "controller-17"; /* Portal assigns the final .local name. */
certConfig.directoryUrl = NULL; /* Derived from the tokengen portal domain. */
certConfig.email = NULL; /* Not used by the SharkCA profile. */
certConfig.acceptTerms = FALSE; /* Not used by the SharkCA profile. */

For IPv4-only enrollment, set certConfig.name = NULL instead. A named enrollment may also include IPv4 if the portal approves it; there is no client-side selector for arbitrary additional identifiers. An identifier is a DNS name or IP address that the certificate is allowed to represent.

Load the trust anchors needed for the HTTPS service of your SharkCA portal into clientSsl. Separately, distribute the private issuing CA's trust anchor to the intended browsers. For named devices, arrange mDNS advertisement using the portal-confirmed .local name; this library does not start an mDNS responder.

5. Read status and handle callbacks

All public calls on an active client require the server's dispatcher mutex. Calls from a BWS request callback already run with this mutex held. An ordinary application task must acquire and release it as in the start helper. Do not call this API from an interrupt service routine. Arrange task-level delivery for interrupt-originated events.

The timer worker releases the mutex around blocking network, storage, and key-generation work so the server can continue serving requests. The host must still serialize its own access to the configuration and shared objects.

Management active is different from HTTPS ready

BaBool SharkTrustX_getStatus(SharkTrustX_Status* status) performs no network or storage work. Its required status output belongs to the caller; all fields are overwritten. The function's return value answers whether management is active. status.ready answers whether at least one managed listener currently has an unexpired certificate. It does not test network reachability or confirm that both IPv4 and IPv6 are available.

Return / field C type Meaning
Function return BaBool TRUE while managing, including initial enrollment; FALSE before start, during cleanup, or after failed cleanup.
status.ready BaBool TRUE when at least one managed listener has an unexpired certificate; otherwise FALSE.
status.expires BaTime Expiry in UTC Unix seconds. Zero when not ready.
status.renew BaTime Certificate lifetime midpoint in UTC Unix seconds. Zero when not ready. May be in the past during renewal or retry. It is not the next retry time.

When management is inactive, all output fields are zero. This helper returns a caller-owned snapshot and can be used from a normal application task:

/* Required pointers: initialized server and caller-owned output structure.
* Call without holding the mutex. The copied data may become stale later. */
BaBool readCertificateStatus(HttpServer* server, SharkTrustX_Status* output)
{
ThreadMutex* mutex = HttpServer_getMutex(server);
BaBool active;
ThreadMutex_set(mutex);
active = SharkTrustX_getStatus(output);
ThreadMutex_release(mutex);
return active;
}
BA_API BaBool SharkTrustX_getStatus(SharkTrustX_Status *status)
Copy current certificate status without network or storage operations.
U8 BaBool
Boolean stored in an unsigned byte; FALSE is zero and TRUE is one.
Definition: GenPrimT.h:118
Snapshot of the certificate currently served by the managed listeners.
Definition: SharkTrustX.h:167

Inside a web-page callback, call SharkTrustX_getStatus() directly under the already-held mutex. Copy other shared display data, including the name and last error, before writing the response: socket writes may release the mutex. examples/SharkTrustX/src/InstallVD.c demonstrates this pattern.

Callback contracts

Callback Arguments and result Context and lifetime
SharkTrustX_Event onEvent void* context, int code; returns void. Positive status or negative error from
  1. Understand errors
.
Runs on the timer worker with the server mutex held. Keep it short and nonblocking. May call getStatus() or request cleanup.
SharkTrustX_Name onName void* context, const char* name, size_t length; returns void. Full NUL-terminated DNS name and its byte length, or NULL, 0 for IP-only SharkCA. Same worker/mutex context. Name is borrowed only for this call: copy it if needed later. Delivered once after confirmation on each start, then only if it changes.
SharkTrustX_Event onClean void* context, int code; returns void. Zero is completed cleanup; negative is failure. Worker with the mutex held for active cleanup. Runs inline in the caller for an inactive store and may occur before clean() returns. The separate cleanup context must survive until completion.
SharkTrustX_Proof proof const unsigned char* message, size_t messageLen, unsigned char proof[32]; returns int, zero on success, nonzero on failure. The worker calls it with the dispatcher mutex released. Treat the input as borrowed binary bytes, write exactly 32 output bytes, and do not retain pointers.

A successful cleanup callback may start a new client. It must first check its code: cleanup acceptance alone is not permission to reuse the old objects. Do not wait inside a callback for another callback or for the timer worker.

Ready reports an installed certificate. It does not assert that the whole attempt has finished: a later enrollment refresh or DNS-record removal can still fail and generate a negative event followed by Retry. Keep readiness and the last error as separate application values, as the example page does.

6. Renewal, restart, and address changes

Renewal is due halfway through the certificate's validity period, calculated from the certificate itself. For example, a certificate valid from October 1 at 00:00 UTC to October 7 at 00:00 UTC is due for renewal on October 4 at 00:00 UTC. The client does not assume a fixed certificate lifetime.

While a renewal attempt fails, an existing usable certificate can continue serving HTTPS. The worker retries temporary failures after 30, 60, 120, 240, then at most 300 seconds. Other failures, including identity, storage, and trust errors, retry hourly. onEvent reports the negative error and then SharkTrustX_Retry, unless cleanup intervenes. An error does not by itself stop management or bypass trust checks.

The worker closes an expired listener when it next checks expiry. Even before that worker check, getStatus() will not report an expired certificate as ready. A blocking platform operation can delay worker progress; do not use the renewal date as a guarantee of availability.

Installing a replacement certificate closes all managed listeners' existing HTTPS connections. Clients must reconnect. Other BWS listeners are unaffected. The example's separate HTTP status listener therefore remains available.

On restart, reuse the same private store and provisioning configuration. The worker checks the saved identity and restores a usable certificate before refreshing enrollment. It does not silently register a new device when saved state is corrupt or belongs to a different identity. There is no public stop-without-erasure function; the desktop example exits the process while retaining files. Do not call clean() merely to shut down.

Tell the client when the device's IPv4 address changes

There is no periodic address discovery. Initially, the client discovers the local IPv4 address of a trusted connection to its portal. Your host should notify it after a network address change. For example, this helper queues rediscovery from an application task after the network is ready:

/* Call without holding the mutex; server is the active client's server. */
int refreshCertificateAddress(HttpServer* server)
{
ThreadMutex* mutex = HttpServer_getMutex(server);
int rc;
ThreadMutex_set(mutex);
rc = SharkTrustX_setIpAddress(NULL); /* Discover on the next portal connection. */
ThreadMutex_release(mutex);
return rc; /* Zero means queued, not that the portal has been updated. */
}
BA_API int SharkTrustX_setIpAddress(const char *address)
Ask the active client to publish a changed local IPv4 address.

int SharkTrustX_setIpAddress(const char* address) also accepts an explicit dotted IPv4 string, such as "192.168.1.50", and copies it before returning. Use four decimal octets from 0 to 255 without leading zeroes. It returns zero when queued, SharkTrustX_Busy without active management, or SharkTrustX_Portal for an invalid address. Updates are serialized with issuance. An approved SharkCA identifier change requires a replacement certificate; a successful address update alone does not emit Ready.

The discovered address is the device's local socket address, not a router's public address. Certificate issuance does not configure routing or port forwarding, and this C client does not provide a reverse connection service.

7. Store keys and enrollment safely

This C implementation stores private keys and device credentials unencrypted. Root the supplied IoIntf in storage restricted to the device or account that runs the application. Never mount it as web content, include it in a downloadable diagnostic bundle, or share it between devices.

Files are stored directly in the root of the supplied IoIntf. The client does not create a subdirectory. This is the root configured for that instance, not necessarily the root of the device's filesystem.

To use a dedicated directory, create it and configure the IoIntf to use it as its root before starting the client. For example, a DiskIo rooted at /data/sharktrustx stores sharktrustx.0 at /data/sharktrustx/sharktrustx.0.

The library owns these three filenames at that root:

File Purpose
sharktrustx.0, sharktrustx.1 Alternating binary state records holding enrollment, account/key material, and certificate. The newest valid committed record is loaded. The previous slot is removed after successful certificate installation.
sharktrustx.reset Durable cleanup marker. Startup finishes an interrupted reset before restoring state.

Supply an initialized IoIntf* backed by a writable persistent filesystem, such as the DiskIo implementation ported to your target. Use that implementation directly; no SharkTrustX-specific storage adapter is needed. Keep the instance and its backing store valid until cleanup finishes, including when cleanup fails.

The client uses standard IoIntf file-open and removal operations, plus read, write, flush, and close on the returned ResIntf file handles. These operations belong to the filesystem implementation and its target port. The client flushes and closes each state file after writing it; persistence across power loss depends on the filesystem, storage device, and port. IoIntf does not itself guarantee physical-media durability.

If you are implementing a new filesystem port, support OpenRes_READ and OpenRes_WRITE (create or truncate), and the file operations listed above. Report an absent file as IOINTF_NOTFOUND or IOINTF_ENOENT. Short reads are supported; a partial or oversized record is rejected.

The records are versioned binary data tied to the C structure layout and byte order. They are not interchangeable with Lua client files or an arbitrary firmware ABI. Checksums detect damaged records; they do not authenticate them against deliberate modification. Storage access controls or platform storage protection remain the host's responsibility.

There are three different kinds of secret involved:

Secret Purpose and ownership
Tokengen provisioning credential Lets this product enroll with the portal. Supplied by the host's generated source.
Device credential Identifies one enrolled device to the portal. Generated by the client and saved before registration for recovery.
ACME account key and certificate private key Separate P-256 software keys generated by the client. The account key signs CA requests; the certificate key serves the device's TLS connections.

The native C client uses SharkSSL software keys. It does not use a TPM like the Lua version.

8. Clean state or change configuration

SharkTrustX_clean() is a destructive local reset, with two levels. Both stop management, close all managed listeners and their connections, and remove the current certificate and its private key. Both deliberately cause an interval without the managed HTTPS service.

State after successful cleanup all = FALSE all = TRUE
Current certificate and certificate key Removed Removed
Completed or pending portal enrollment Retained Removed locally
Public-profile ACME account/provider state Removed, allowing a provider change Removed
SharkCA account key, account registration, and directory binding Retained, because enrollment is bound to this account Removed locally
Host server, timer, storage adapter, outbound TLS context Remain host-owned Remain host-owned

Use ordinary cleanup to change a public ACME provider while retaining portal enrollment. Use full cleanup for a different portal/provisioning identity, preferred label, build profile, or SharkCA account/directory binding. A full local cleanup does not unregister the old portal device or revoke its certificate.

The signature is:

/* Request acceptance and cleanup completion are separate results. */
SharkTrustX_Event onClean, void* context);
void(* SharkTrustX_Event)(void *context, int code)
Receive one numeric event.
Definition: SharkTrustX.h:105
BA_API int SharkTrustX_clean(IoIntf *io, BaBool all, SharkTrustX_Event onClean, void *context)
Stop work, close the managed listener, and erase owned certificate state.

io is the required store used by the active client, or an inactive store. all is the required BaBool selection above. onClean is optional, but use it when your application needs to know that storage erasure succeeded. context is an optional borrowed application pointer for this callback.

The int return is zero when accepted, SharkTrustX_Busy for a conflicting cleanup/store, or SharkTrustX_Storage for a NULL store. Once accepted, onClean(context, 0) reports completed cleanup; a negative callback code reports failure. For an inactive store, storage work and the callback occur synchronously before clean() returns, but the return still reports acceptance. Serialize inactive-store cleanup with the application's lifecycle too.

Restart only after the completion callback

This continuation builds on the static certConfig from the start helper. It requests an ordinary reset and then starts again with the same provider. It illustrates where a public provider change belongs without introducing an unconfigured CA into the example.

static void restartAfterClean(void* context, int code)
{
int rc;
if(code != 0)
{
HttpTrace_printf(0, "Cleanup failed: %d; management remains stopped.\n", code);
return; /* Retain cfg and its objects so cleanup can be retried. */
}
/* Active-client cleanup calls here with the mutex held. It is now safe
* to change cfg->directoryUrl and accepted terms for another public CA.
* This example keeps the existing staging provider. */
rc = SharkTrustX_start(cfg);
if(rc != 0)
HttpTrace_printf(0, "Restart rejected: %d\n", rc);
}
/* Call from an application task without the server mutex already held.
* certConfig and its host objects were initialized by startCertificates(). */
int resetCertificateAndRestart(void)
{
ThreadMutex* mutex = HttpServer_getMutex(certConfig.server);
int rc;
ThreadMutex_set(mutex);
rc = SharkTrustX_clean(certConfig.io, FALSE, restartAfterClean, &certConfig);
ThreadMutex_release(mutex);
return rc; /* The callback, not this return, reports completed erasure. */
}

For an actual provider change, keep the new URL in persistent application memory, confirm its terms, and prepare the needed outbound CA anchors before starting again. Do not modify borrowed configuration while the old client is still running. There is no simultaneous old/new provider management or rollback to the erased certificate.

Cleanup can be requested during issuance or from onEvent/onName. It marks the client stopped, closes its HTTP connection and listener, and lets the worker finish. Do not block the dispatcher waiting for it. A platform DNS or connect operation may need to return before the worker can complete cleanup.

Successful completion means no old event/name callback, state write, or listener installation can follow. After failure, management stays stopped and start() remains blocked until cleanup succeeds. Retain the old objects and retry cleanup on the same store after correcting the storage problem.

The reset marker allows cleanup to resume after power loss; deletion is not one atomic filesystem operation. Normal public issuance attempts to remove its DNS TXT challenge record on success or failure. Explicit clean() does not start remote TXT removal or wait for the old portal, so an interrupted challenge can leave a record there.

9. Understand errors

Read an immediate API return separately from a later event. A rejected start has no new management instance and produces no onEvent notification for that rejection. After acceptance, the worker reports operational errors using onEvent and keeps retrying until cleanup. Cleanup results go to onClean.

Code C enum constant Meaning and first check
1 SharkTrustX_Ready Certificate installed or restored. Check getStatus() for current readiness and dates.
2 SharkTrustX_Retry A retry is scheduled after the preceding negative event. Preserve that error in your logs/UI.
-1 SharkTrustX_Busy Start/cleanup lifecycle conflict, or address update without active management. Wait for cleanup completion rather than starting again.
-2 SharkTrustX_Identity Saved identity, requested label, profile, or directory differs. Restore the original configuration or perform the appropriate explicit cleanup.
-3 SharkTrustX_Storage State read, commit, recovery, or cleanup failed. Check permissions, capacity, record compatibility, and adapter flush/close errors. Corrupt enrollment is not automatically replaced.
-4 SharkTrustX_Portal Invalid configuration/address or portal/DNS operation failed. For immediate rejection check required fields and timer mutex. For an event check network, portal configuration, and matching tokengen credentials.
-5 SharkTrustX_Acme Account, order, authorization, or certificate download failed. Check CA availability and whether its requirements fit the selected profile.
-6 SharkTrustX_Trust Outbound HTTPS failed trust/name/date validation. Check UTC time, CA anchors, and the remote certificate's name and validity. Keep validation enabled.
-7 SharkTrustX_Crypto Allocation, key generation/signing, certificate validation, timer scheduling, or listener installation failed. Check heap, entropy, SharkSSL features, and port availability.
-9 SharkTrustX_Terms Public start rejected because terms were not accepted. Set acceptTerms only after acceptance.

There is no public -8 enum value. Negative events are categories, not HTTP status codes. A Retry event does not identify the next retry time, and status.renew is not a retry timer.

If the browser warns but status.ready is true, check browser trust and the URL first. In the public profile, use the assigned DNS name, not localhost or a raw IP address. Staging certificates still produce trust warnings even at the correct name. For SharkCA, check private CA installation and name resolution independently of certificate issuance.

10. Build dependencies and scope

Compile amalgamation/BWS.c with SHARKTRUSTX, BA_JSON=1, and SHARKSSL_ENABLE_CERTINFO_PARSE_API=1. This includes xrc/misc/SharkTrustX.c and the required HttpClient, BaTimer, and SharkSSL CSR implementations. Compile your generated tokengen source with TOKENGEN_C_API. Do not compile SharkTrustX.c separately or link duplicate copies of these implementations or the BAS amalgamation into the example.

SHARKTRUSTX enables the native C client in BWS. Leave it undefined to omit the client and these dependencies. BAS_LOADED controls separate BAS/Lua features and does not enable SharkTrustX.

SharkSSL needs client/server TLS, SHA-256, P-256 elliptic-curve keys, ECDSA signing (the ACME ES256 algorithm), PEM key/certificate conversion, and CSR creation. In particular, the client checks SHARKSSL_ENABLE_CSR_CREATION, SHARKSSL_ENABLE_ECCKEY_CREATE, SHARKSSL_ENABLE_PEM_API, and SHARKSSL_ENABLE_CERTINFO_PARSE_API. The public SharkSslCertInfo_parseCert() API reads the saved certificate's dates, CA flag, and subject alternative names. PEM is the text encoding used for certificates and keys; DER is their binary encoding. The client uses BWS's configured baMalloc/baFree allocator.

Use the example guide for target selection, persistent storage, and the standard void barracuda(void) example entry point. Standalone builds use the shared Main.c and HostInit.c; RTOS applications call barracuda() from their usual server task. Port selection does not initialize your board's network, clock, storage, or entropy source.

Current limit Value or supported behavior
Active clients/listeners One client per process; up to two HTTPS listeners sharing one port, TLS context, and certificate. IPv6 is tried first when USE_IPV6 is enabled, then IPv4.
Account and certificate keys Separate P-256 software keys; no algorithm selector.
ACME URL At most 767 bytes, excluding the terminator.
Downloaded certificate chain At most 16,383 PEM bytes, excluding the terminator.
HTTP response body At most 32,767 bytes.
Issuance polling Ten-minute deadline, checked between operations; not a hard upper bound on blocking platform DNS/connect calls.
Renewal policy Certificate lifetime midpoint; no ACME Renewal Information (ARI) support.
Public CA compatibility DNS-01 with ES256 accounts. No External Account Binding (EAB), HTTP-01, RSA account keys, wildcard names, or general multi-domain requests.
Addressing and optional services IPv4 portal registration; IPv4/IPv6 HTTPS according to the enabled BWS network port and successful listener attempts. No IPv6 certificate identifiers, reverse connections, revocation API, or public stop-without-erasure API.

The client checks that the installed leaf certificate is current, is not a CA certificate, matches its private key, and has exactly the approved subject alternative names (SANs). SANs are the certificate's list of permitted names and addresses. These local checks do not validate the issuer chain. Outbound HTTPS authenticates the service used to obtain the certificate; the connecting browser independently decides whether it trusts the issued chain.