nomyo-js/doc/getting-started.md
alpha nerd 111335d7ff
fix: correct package entry points and publish contents
Five packaging defects, all pre-existing:

1. dist/esm/index.js held ESM syntax while the package is not
   "type": "module", so Node classified it as CommonJS. It failed
   outright on Node 18 ("Unexpected token 'export'") and only worked on
   Node >= 22 because Node re-parses after guessing the module type,
   paying that cost on every import. Bundles now carry explicit
   extensions: .mjs for ES output, .cjs/.js for CommonJS. The browser
   build gained a real CommonJS output too — the exports map previously
   pointed the browser "require" condition at an ES module.
   The exports map now also leads with "types" and ends with a "default"
   fallback for resolvers matching neither "node" nor "browser".

2. files: ["native"] published the local build directory: a 94.6 kB
   Linux-x64 .node binary, a 148 kB object file and generated Makefiles.
   node-gyp-build checks build/Release before prebuilds, so every
   consumer on every platform would have found this machine's binary,
   skipped compiling, and failed to load it. It fails safe (native/
   index.js catches and returns null), but the addon could never work
   for anyone else. Narrowed to the four source files.

3. binding.gyp resolves node-addon-api at build time, but nothing
   declared it: it was a devDependency of the root, absent from
   native/package.json. The build only succeeded here because a dev
   install populates the root node_modules. Declared as a dependency of
   the native package, where it is actually needed.

4. No clean step, so stale output shipped — the tarball carried both
   dist/types/core/** and a dist/types/src/** tree left over from before
   rootDir was set. build now runs clean first.

5. test:browser ran `karma start` with no karma.conf.js anywhere in the
   repo, and tests/browser is an empty directory. Removed the script and
   the karma devDependency rather than leave a script that cannot run.

Verified: CommonJS require and ESM import both resolve on Node 18.19.1
and 24.18.0; TypeScript resolves types under both bundler and node16;
npm pack now produces 35 files / 103.8 kB with no build artefacts.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-19 12:39:09 +02:00

7.5 KiB

Getting Started

Overview

NOMYO.js provides end-to-end encryption for all communication between your application and NOMYO inference endpoints. Your prompts and responses are encrypted before leaving your process and decrypted only after they arrive back — the server never sees plaintext.

The API mirrors OpenAI's ChatCompletion, making it easy to integrate into existing code.

Note on streaming: The API is non-streaming. Setting stream: true in a request is ignored server-side to maintain full response encryption.


Simple Chat Completion

import { SecureChatCompletion } from 'nomyo-js';

const client = new SecureChatCompletion({
  apiKey: process.env.NOMYO_API_KEY,
});

const response = await client.create({
  model: 'Qwen/Qwen3-0.6B',
  messages: [{ role: 'user', content: 'Hello! How are you today?' }],
  temperature: 0.7,
});

// Extract what you need, then let the response go out of scope promptly.
// This minimises the time decrypted data lives in process memory
// (reduces exposure from swap files, core dumps, or memory inspection).
const reply = response.choices[0].message.content;
console.log(reply);

With a System Message

const response = await client.create({
  model: 'Qwen/Qwen3-0.6B',
  messages: [
    { role: 'system', content: 'You are a helpful assistant.' },
    { role: 'user',   content: 'What is the capital of France?' },
  ],
  temperature: 0.7,
});

console.log(response.choices[0].message.content);

API Key Authentication

// Constructor-level key (used for all requests from this instance)
const client = new SecureChatCompletion({
  apiKey: process.env.NOMYO_API_KEY,
});

// Per-request key override (takes precedence over constructor key)
const response = await client.create({
  model: 'Qwen/Qwen3-0.6B',
  messages: [{ role: 'user', content: 'Hello!' }],
  api_key: 'override-key-for-this-request',
});

Security Tiers

Pass security_tier in the request to control hardware routing and isolation level:

Tier Use case
"standard" General secure inference (GPU)
"high" Sensitive business data — enforces secure tokenizer
"maximum" HIPAA PHI, classified data — E2EE, maximum isolation
// Standard — general use
const response = await client.create({
  model: 'Qwen/Qwen3-0.6B',
  messages: [{ role: 'user', content: 'General query' }],
  security_tier: 'standard',
});

// High — sensitive business data
const response2 = await client.create({
  model: 'Qwen/Qwen3-0.6B',
  messages: [{ role: 'user', content: 'Summarise this contract clause...' }],
  security_tier: 'high',
});

// Maximum — PHI / classified data
const response3 = await client.create({
  model: 'Qwen/Qwen3-0.6B',
  messages: [{ role: 'user', content: 'Patient record summary...' }],
  security_tier: 'maximum',
});

Using "high" or "maximum" adds latency vs "standard" due to additional isolation measures.


Using Tools (Function Calling)

const response = await client.create({
  model: 'Qwen/Qwen3-0.6B',
  messages: [{ role: 'user', content: "What's the weather in Paris?" }],
  tools: [
    {
      type: 'function',
      function: {
        name: 'get_weather',
        description: 'Get weather information for a location',
        parameters: {
          type: 'object',
          properties: {
            location: { type: 'string', description: 'City name' },
          },
          required: ['location'],
        },
      },
    },
  ],
  tool_choice: 'auto',
  temperature: 0.7,
});

const message = response.choices[0].message;
if (message.tool_calls) {
  const call = message.tool_calls[0];
  console.log('Tool called:', call.function.name);
  console.log('Arguments:', call.function.arguments);
}

Error Handling

Import typed error classes to distinguish failure modes:

import {
  SecureChatCompletion,
  AuthenticationError,
  RateLimitError,
  InvalidRequestError,
  ForbiddenError,
  ServerError,
  ServiceUnavailableError,
  APIConnectionError,
  SecurityError,
} from 'nomyo-js';

const client = new SecureChatCompletion({ apiKey: process.env.NOMYO_API_KEY });

try {
  const response = await client.create({
    model: 'Qwen/Qwen3-0.6B',
    messages: [{ role: 'user', content: 'Hello!' }],
  });
  console.log(response.choices[0].message.content);

} catch (err) {
  if (err instanceof AuthenticationError) {
    console.error('Bad API key:', err.message);

  } else if (err instanceof RateLimitError) {
    // The client already retried automatically (default: 2 retries).
    // If you reach here, all retries were exhausted.
    console.error('Rate limit exceeded after retries:', err.message);

  } else if (err instanceof ForbiddenError) {
    // Model not allowed for the requested security_tier
    console.error('Forbidden:', err.message);

  } else if (err instanceof InvalidRequestError) {
    console.error('Bad request:', err.message);

  } else if (err instanceof ServerError || err instanceof ServiceUnavailableError) {
    console.error('Server error (retries exhausted):', err.message);

  } else if (err instanceof APIConnectionError) {
    console.error('Network error (retries exhausted):', err.message);

  } else if (err instanceof SecurityError) {
    console.error('Encryption/decryption failure:', err.message);

  } else {
    throw err;   // re-throw unexpected errors
  }
}

All typed errors expose:

  • message: string — human-readable description
  • statusCode?: number — HTTP status (where applicable)
  • errorDetails?: object — raw response body (where applicable)

Resource Management

Always call dispose() when you're done with a client to stop the background key-rotation timer and zero in-memory key material:

const client = new SecureChatCompletion({ apiKey: process.env.NOMYO_API_KEY });

try {
  const response = await client.create({ ... });
  console.log(response.choices[0].message.content);
} finally {
  client.dispose();
}

For long-running servers (HTTP handlers, daemons), create one client instance and reuse it — don't create a new one per request.


acreate() Alias

acreate() is a direct alias for create() provided for code that follows the OpenAI naming convention:

const response = await client.acreate({
  model: 'Qwen/Qwen3-0.6B',
  messages: [{ role: 'user', content: 'Hello!' }],
});

Browser Usage

In browsers, keys are kept in memory only (no file system). Everything else is identical to Node.js.

<script type="module">
  import { SecureChatCompletion } from 'https://unpkg.com/nomyo-js/dist/browser/index.mjs';

  const client = new SecureChatCompletion({
    baseUrl: 'https://api.nomyo.ai',
    apiKey: 'your-api-key',
  });

  const response = await client.create({
    model: 'Qwen/Qwen3-0.6B',
    messages: [{ role: 'user', content: 'Hello from the browser!' }],
  });

  console.log(response.choices[0].message.content);
  client.dispose();
</script>

Security note: Embedding API keys in browser-side code exposes them to end users. In a real application, proxy requests through your backend or use short-lived tokens.


Local Development (HTTP)

For a local NOMYO router running over plain HTTP:

const client = new SecureChatCompletion({
  baseUrl: 'http://localhost:12435',
  allowHttp: true,   // required; prints a security warning
});

Never use allowHttp: true in production.