Account authority
A Softchat product account has one locally held authority. Import exactly 32 secret bytes once, clear the caller buffer, and retain only the opaque account or identity capability for the authenticated session. The pre-1.0 product API supports local authority only.
Local identity lifecycle
identity.lifecycleImport, retain, and explicitly end one local account authority.
- Inputs and exact bounds
- exactly 32 secret bytes
- Output
- An opaque authority capability and canonical cached public key.
- Trust and authentication
- The capability is trusted local authority; caller buffers are cleared or remain explicitly caller-owned according to the language contract.
- Stable errors
invalid_secret_key,identity_erased,account_closed· handling and retry rules
Ownership, lifecycle, and execution rules
- Ownership
- Rust owns protocol semantics, Android account SQLite/use-case state, and optional native relay I/O; the host owns account lifecycle, HTTP/media/background effects, files, and UI.
- Retry
- Retry only host transport or explicitly retryable session work; validation and authentication failures are permanent for the same input.
- Lifecycle
- Language ownership is automatic; explicitly cancel or erase only at the operation-specific authority boundary.
- Concurrency
- Serialize mutable session capabilities; immutable values follow the language type system.
- Cancellation
- The host owns async cancellation; use cancel, shutdown, close, or erase only where exposed.
Exact public symbols
- Rust
- Android
- Swift
- TypeScript
LocalIdentity::from_secret_bytesLocalIdentity::public_keySoftchatAccount.openSoftchatAccount.publicKeySoftchatAccount.closeSoftchatIdentity.init(secretKey:)SoftchatIdentity.publicKeySoftchatIdentity.eraseSoftchatIdentity.importSoftchatIdentity.publicKeySoftchatIdentity.erase- Rust
- Android
- Swift
- TypeScript
let identity = LocalIdentity::from_secret_bytes(&secret)?;
secret.fill(0);
let author = identity.public_key();
// Use throughout this account session.
drop(identity); // logout; Rust ownership drops the secret-bearing state
Use LocalIdentity in Rust. It is not cloneable and relies on deterministic
Rust ownership rather than an erase() method. Put it in an Option when an
account session needs to take and drop authority explicitly.
LocalIdentityHandle is the coarse generated-binding owner, not the preferred
native API.
val account = credentialStore.withSecret(accountReference) { temporary ->
SoftchatAccount.open(
secretKey = temporary,
databaseDirectory = context.noBackupFilesDir,
)
}
val author = account.publicKey
// Close before switching or deleting the account.
account.close()
open clears the temporary mutable buffer on both success and failure. Android
owns Keystore policy and encrypted credential persistence; Rust derives the
public key, database identity, and filename. close() waits for in-flight
calls, closes SQLite, and erases the in-memory authority.
let identity = try SoftchatIdentity(secretKey: secret)
secret.resetBytes(in: secret.indices)
let author = try identity.publicKey
identity.erase()
ARC owns normal lifetime. erase() is an explicit authority transition, not
an alternative to ARC.
const identity = SoftchatIdentity.import(secret);
secret.fill(0);
const author = identity.publicKey;
identity.erase();
identity.erase();
Do not retain the original Uint8Array; imported authority remains inside the
opaque Wasm-backed object.
- Rust
- Android
- Swift
- TypeScript
The public-key accessor is cached in foreign facades. Its benchmark is below the useful clock floor and is recorded only as an API-shape check.
- Rust
- Android
- Swift
- TypeScript
Safety checklist
- Never log, stringify, clone broadly, or persist secret bytes.
- Never use example fixture keys for an account.
- Never cache derived conversation keys to optimize a benchmark.
- Treat an erased identity as permanently invalid; create a new one after reauthentication.
- Keep one consistent caller/actor when an application permits concurrent access.