@elysea/core
v0.8.0
Published
SDK ELYSÉA — cerveau éthique cross-app. Guardian SDK, pipeline canon 9 étapes, mémoire souveraine, identité universelle. TypeScript pur, zero browser deps.
Maintainers
Readme
@elysea/core
Guardian SDK for ethical AI — deterministic enforcement, sovereign memory, universal identity.
npm install @elysea/coreTable of contents
- First call
- Mock mode
- Access keys
- Error handling
- Timeout and AbortSignal
- Guardian SDK
- Trust mark and verification
- Debug
- Key exports
- Versioning policy
- Documentation
First call
import { createElyseaClient } from '@elysea/core';
const elysea = createElyseaClient({
// URL and credentials provided by ELYSÉA on builder account activation
baseUrl: process.env.ELYSEA_BASE_URL!,
auth: {
apiKey: process.env.ELYSEA_API_KEY!, // sk_test_… or sk_live_…
apiSecret: process.env.ELYSEA_API_SECRET!, // HMAC secret
},
appId: process.env.ELYSEA_APP_ID!,
countryCode: 'FR', // ISO 3166-1 alpha-2 — required (see below)
region: 'EU', // 'EU' | 'US' — data sovereignty
});
// 1. Identity resolution
const identity = await elysea.identity.resolve({ userJwt: 'user-jwt-here' });
// 2. Canonical pipeline with Guardian enforcement
const result = await elysea.pipeline.run({
userInput: 'Hello',
coreUserId: identity.coreUserId,
});
if (result.guardianAction === 'pass') {
console.log(result.response);
} else if (result.guardianAction === 'block') {
console.log(result.errorCode); // e.g. 'GUARDIAN_BLOCK'
console.log(result.errorMessage); // human-readable message
console.log(result.errorAction); // recommended remediation
}createElyseaClient configuration
| Field | Type | Required | Description |
|----------------------|-------------------------|----------|-------------|
| baseUrl | string | Yes | Core API base URL |
| auth.apiKey | string | Yes | sk_test_… or sk_live_… |
| auth.apiSecret | string | Yes | HMAC signing secret |
| appId | string | Yes | Builder application identifier |
| countryCode | string | Yes | ISO 3166-1 alpha-2 (e.g. 'FR', 'DE'). Required for crisis resource routing. Throws ElyseaMissingCountryCodeError if absent. |
| region | 'EU' \| 'US' | No | Data sovereignty region |
| llmProvider | { provider: string } | No | LLM provider to use (e.g. { provider: 'openai' }). The LLM API key must be deposited once in the ELYSÉA developer portal — it is never transmitted in SDK headers. Pipeline calls without a registered key return 422 BYOK_KEY_REQUIRED. Requires no apiKey in the object — key storage is handled server-side. |
| defaultTimeoutMs | number | No | Default: 30000 (30 s). Applied to every call unless overridden per-call. |
| debug | boolean | No | Log HTTP calls to console.debug. Never enable in production. |
Mock mode
No credentials needed. Use the built-in mock client to develop offline or write tests without a live Core instance.
import { createMockElyseaClient } from '@elysea/core';
const elysea = createMockElyseaClient({ appId: 'my-app' });
// Same API surface as the live client. Swap one line to go live.
const result = await elysea.pipeline.run({ userInput: 'Hello', coreUserId: 'u_123' });Access keys
| Format | Environment | How to obtain |
|---------------|-----------------------------------|---------------|
| sk_test_… | Sandbox — no billing | elysea.app/developers |
| sk_live_… | Production | Issued after builder account audit |
Error handling
pipeline.run() never throws — it always returns a PartnerInvokeResult. On a block (guardianAction: 'block'), three fields indicate the cause:
const result = await elysea.pipeline.run({ userInput, coreUserId });
if (result.guardianAction === 'block') {
switch (result.errorCode) {
case 'AUTH_INVALID_KEY': // key empty or missing sk_test_/sk_live_ prefix
case 'AUTH_UNAUTHORIZED': // invalid key or wrong HMAC secret
case 'AUTH_FORBIDDEN': // insufficient permissions for this tier
case 'RATE_LIMITED': // retries exhausted on HTTP 429
case 'QUOTA_CONVERSATION': // conversation history exceeds 20 turns
case 'GUARDIAN_BLOCK': // ethical block — see guardianLog.pillar
case 'NETWORK_ERROR': // network unavailable or timeout
case 'SERVER_ERROR': // Core 5xx or malformed SSE
case 'MISSING_COUNTRY_CODE': // countryCode absent at init
case 'MISSING_CONSENT_TOKEN': // P7 — consent token missing
console.log(result.errorMessage, result.errorAction);
}
}Synchronous errors (bad config at boot) throw ElyseaSDKError before any network call:
import { ElyseaSDKError } from '@elysea/core';
try {
const elysea = createElyseaClient({
auth: { apiKey: '', apiSecret: '' },
// ...
});
} catch (err) {
if (err instanceof ElyseaSDKError) {
console.log(err.code); // e.g. 'AUTH_INVALID_KEY'
console.log(err.action); // recommended action
}
}Error code reference
| Code | Cause | Recommended action |
|-------------------------|----------------------------------------------------------|--------------------|
| AUTH_INVALID_KEY | Key is empty or lacks sk_test_/sk_live_ prefix | Check ELYSEA_API_KEY |
| AUTH_UNAUTHORIZED | HTTP 401 — key invalid or HMAC secret wrong | Verify key + secret |
| AUTH_FORBIDDEN | HTTP 403 — insufficient permissions | Check account tier |
| RATE_LIMITED | Retries exhausted on HTTP 429 | Back off and retry |
| QUOTA_CONVERSATION | conversationHistory exceeds 20 turns | Trim history |
| GUARDIAN_BLOCK | Ethical block, prohibitions P1–P8 | Inspect guardianLog.pillar |
| NETWORK_ERROR | Network unreachable or timeout elapsed | Check ELYSEA_BASE_URL |
| SERVER_ERROR | Core 5xx or malformed SSE stream | Retry after a few seconds |
| MISSING_COUNTRY_CODE | countryCode absent at createElyseaClient | Add countryCode: 'FR' |
| MISSING_CONSENT_TOKEN | P7 — consent token missing from request | Generate via createConsentToken() |
| BYOK_KEY_REQUIRED | HTTP 422 — no LLM key registered for this app in the portal | Deposit a key at elysea.app/developers → LLM Keys |
Timeout and AbortSignal
Every pipeline.run() call cancels after 30 seconds by default — the client never hangs indefinitely. A cancelled call returns:
{ guardianAction: 'block', errorCode: 'NETWORK_ERROR', guardianDegraded: true }Configure at the client level, per call, or via an AbortSignal:
// Global default (all calls)
const elysea = createElyseaClient({ ..., defaultTimeoutMs: 15_000 });
// Per-call override
const result = await elysea.pipeline.run({
userInput,
coreUserId,
timeoutMs: 5_000,
});
// AbortSignal — e.g. React component unmount
const controller = new AbortController();
useEffect(() => () => controller.abort(), []);
const result = await elysea.pipeline.run({
userInput,
coreUserId,
signal: controller.signal,
});Guardian SDK
ELYSÉA enforces 8 prohibitions (P1–P8) on every call — intelligent detection, deterministic blocking. These invariants cannot be disabled, overridden, or bypassed by builder configuration.
| Violation | guardianAction | errorCode |
|-----------------------|------------------|-------------------|
| Any prohibition P1–P8 | 'block' | 'GUARDIAN_BLOCK' |
On a Guardian block:
result.guardianLog.pillarexposes which invariant was triggered.- Internal matching logic is not exposed to the caller.
- The block is never surfaced as HTTP 403 — it is a first-class SDK result.
Use enforceGuardian(input) to check prohibitions without instantiating a full client:
import { enforceGuardian, GuardianViolationError } from '@elysea/core';
try {
enforceGuardian({ userInput: rawInput });
} catch (err) {
if (err instanceof GuardianViolationError) {
console.log(err.pillar); // triggered prohibition
}
}Use addRestriction(check) to add app-level restrictions on top of the 8 ELYSÉA prohibitions. The wrapper guarantees monotonicity: your check runs after the Guardian — it can only tighten scope, never loosen it.
import { addRestriction } from '@elysea/core';
const guardWithScope = addRestriction((input) => {
const msg = typeof input === 'object' && input !== null
? String((input as Record<string, unknown>).userInput ?? '')
: '';
if (['crypto', 'finance'].some((t) => msg.includes(t))) {
throw new Error('Out of scope for this app.');
}
});
// App restriction fires for out-of-scope topics
guardWithScope({ userInput: 'Tell me about crypto.' }); // throws — app restriction
// ELYSÉA Guardian fires for all 8 prohibitions regardless
guardWithScope({ config: { suppressSafety: true } }); // throws GuardianViolationError P3Trust mark and verification
New in v0.7.0: a stable API for reading and creating verification status from attestations.
import { extractVerificationStatus, createVerificationStub } from '@elysea/core';
// Returns: 'verified_public_measures' | 'not_verified' | 'expired' | 'unavailable' | 'error'
const status = extractVerificationStatus(attestation);
const stub = createVerificationStub({ appId: 'my-app' });Deprecated aliases (removed at 1.0.0):
import { extractCertificationStatus } from '@elysea/core'; // @deprecated — use extractVerificationStatusDebug
// Option 1: client config
const elysea = createElyseaClient({ ..., debug: true });
// Option 2: environment variable
// DEBUG=elysea:* node app.jsHTTP requests are logged via console.debug. Do not enable in production.
Key exports
| Export | Description |
|---------------------------|-------------|
| createElyseaClient | Create an authenticated live client |
| createMockElyseaClient | Mock client for offline development and testing |
| enforceGuardian(input) | Check all 8 prohibitions without a client |
| addRestriction(check) | Wrap a custom check on top of the 8 prohibitions — monotonicity guaranteed |
| GuardianViolationError | Type Guardian errors in try/catch |
| ElyseaSDKError | Synchronous error with .code and .action |
| ERROR_CATALOGUE | Messages and recommended actions for all 10 error codes |
| ElyseaPosture | Union type of known postures returned by Core |
| getTierConfig(tier) | Retrieve configuration for a given SDK tier |
| extractVerificationStatus | Parse verification status from an attestation |
| createVerificationStub | Create a verification stub for testing |
| SDK_VERSION | Current semver string |
ElyseaPosture known values:
import type { ElyseaPosture } from '@elysea/core';
// 'firm_safety' | 'present_neutral' | 'empathetic' | 'direct'
// | 'active_listening' | 'bienveillant' | 'objectifs_declares'Versioning policy
@elysea/core follows Semantic Versioning:
| Increment | Criteria | |------------|----------| | patch (0.x.y) | Bug fixes, error messages, documentation | | minor (0.x.0) | Backward-compatible additions; deprecations documented in CHANGELOG | | major (x.0.0) | Breaking API changes; migration guide in CHANGELOG |
TypeScript-only changes (wider unions, added optional fields) do not constitute a breaking change.
Documentation
License
MIT
