npm package: @shiftlabsas/iman-agent
Description: SDK for making your app manageable by IMAN (Instance Manager)
Latest version: 0.1.1

# @shiftlabsas/iman-agent

SDK for making your app manageable by [IMAN](https://github.com/ShiftLabSAS/iman) (Instance Manager). Install it in any Express-compatible app to expose health, manifest, and heartbeat endpoints that IMAN reads and displays in its control panel.

> **Demo:** See [polarities](https://github.com/ShiftLabSAS/polarities/tree/main) for a full working example of an Express app managed by IMAN.

---

## How it works

Once installed, the agent:

1. Mounts `/.iman/*` routes protected by HMAC signature validation — IMAN uses these to fetch health and the manifest.
2. Pushes periodic heartbeats to your IMAN server (signed, never sending the raw token) so the instance stays marked as online.
3. Declares a **manifest** — a capability contract that tells IMAN what actions (metrics, backups, deployments…) are available for this instance.

---

## Installing the agent in your app

```bash
npm install @shiftlabsas/iman-agent
# or
pnpm add @shiftlabsas/iman-agent
```

### Minimal setup (Express)

```ts
import express from 'express';
import { ImanAgent, ManifestBuilder, Duration } from '@shiftlabsas/iman-agent';

const manifest = new ManifestBuilder()
  .setName('my-app')
  .setVersion('1.0.0')
  .setEnv(process.env.NODE_ENV ?? 'production')
  .setCapabilities({ metrics: true })
  .build();

const agent = new ImanAgent({
  instanceId: process.env.IMAN_INSTANCE_ID!, // instance ID issued by IMAN on registration
  token: process.env.IMAN_TOKEN!,            // shared secret — never sent over the wire
  manifest,
  imanUrl: process.env.IMAN_URL,             // e.g. https://iman.example.com
  heartbeatInterval: Duration.minutes(1),
  logLevel: 'info',
});

const app = express();
app.use(agent.expressMiddleware());
agent.startHeartbeat();

app.listen(3000);
```

---

## Environment variables

| Variable           | Required      | Description                                            |
| ------------------ | ------------- | ------------------------------------------------------ |
| `IMAN_INSTANCE_ID` | Yes           | Instance ID issued when the instance was registered in IMAN |
| `IMAN_TOKEN`       | Yes           | Shared secret used to sign requests — never sent over the wire |
| `IMAN_URL`         | For heartbeat | Base URL of the IMAN server (e.g. `https://iman.example.com`) |

---

## API reference

### `ImanAgent`

```ts
new ImanAgent(options: ImanAgentOptions)
```

| Option              | Type                              | Required      | Default                | Description                                             |
| ------------------- | --------------------------------- | ------------- | ---------------------- | ------------------------------------------------------- |
| `instanceId`        | `string`                          | Yes           | —                      | Instance ID issued by IMAN — sent in plaintext          |
| `token`             | `string`                          | Yes           | —                      | Shared secret used to sign requests — never transmitted |
| `manifest`          | `ImanManifest`                    | Yes           | —                      | Capability contract built with `ManifestBuilder`        |
| `imanUrl`           | `string`                          | For heartbeat | —                      | Base URL of your IMAN server                            |
| `heartbeatInterval` | `Duration`                        | No            | `Duration.seconds(30)` | How often to push heartbeats                            |
| `logLevel`          | `'debug' \| 'info' \| 'silent'`   | No            | `'info'`               | Console verbosity                                       |

#### Methods

- **`expressMiddleware()`** — Returns Express middleware that handles all `/.iman/*` routes. Mount it before your app routes.
- **`startHeartbeat()`** — Begins sending signed heartbeats to `imanUrl`. Requires `imanUrl` to be set.

#### Routes exposed

| Route                 | Description                          |
| --------------------- | ------------------------------------ |
| `GET /.iman/manifest` | Returns the full manifest JSON       |
| `GET /.iman/health`   | Returns `{ status: "ok", version }`  |

All routes require a valid `X-IMAN-Token` header.

---

### `ManifestBuilder`

Fluent builder for `ImanManifest`. All fields are set via chainable methods; `.build()` validates and returns the manifest.

```ts
import { ManifestBuilder, DatabaseType, CacheType } from '@shiftlabsas/iman-agent';

const manifest = new ManifestBuilder()
  .setName('my-app')
  .setVersion('2.3.1')
  .setEnv('production')
  .setCapabilities({
    metrics: true,
    settings: { read: true, write: ['theme', 'notifications'] },
    backup: true,
    deployments: { githubRepo: 'ShiftLabSAS/my-app', trigger: true },
  })
  .setDependencies({
    databases: [{ type: DatabaseType.PSQL, label: 'primary', host: 'db.internal' }],
    caches:    [{ type: CacheType.REDIS,   host: 'redis.internal' }],
  })
  .setEndpoints(['/api', '/webhooks'])
  .build();
```

#### Capabilities

| Field                    | Type       | Description                   |
| ------------------------ | ---------- | ----------------------------- |
| `metrics`                | `boolean`  | Expose metrics to IMAN        |
| `settings.read`          | `boolean`  | Allow IMAN to read settings   |
| `settings.write`         | `string[]` | Setting keys IMAN may write   |
| `backup`                 | `boolean`  | Support backup trigger        |
| `deployments.githubRepo` | `string`   | Source repo (`org/name`)      |
| `deployments.trigger`    | `boolean`  | Allow IMAN to trigger deploys |

#### Dependency types

| Enum           | Values                                           |
| -------------- | ------------------------------------------------ |
| `DatabaseType` | `SQLITE`, `MYSQL`, `PSQL`, `MONGODB`, `DYNAMODB` |
| `CacheType`    | `REDIS`, `MEMCACHED`                             |
| `QueueType`    | `SQS`, `RABBITMQ`, `KAFKA`                       |
| `StorageType`  | `S3`, `GCS`, `AZURE`                             |

---

### `Duration`

Convenience wrapper for heartbeat intervals.

```ts
Duration.seconds(30)
Duration.minutes(5)
Duration.hours(1)
Duration.from({ minutes: 2, seconds: 30 }) // 150s
```

---

## Security

Heartbeats are signed with HMAC-SHA256 — the raw token never travels over the wire. Each request includes:

- `X-IMAN-Instance-ID` — identifies the instance (not sensitive)
- `X-IMAN-Timestamp` — Unix timestamp in seconds
- `X-IMAN-Signature` — `sha256=HMAC(token, "{timestamp}.{instanceId}")`

IMAN rejects requests with a timestamp older than 5 minutes, preventing replay attacks.

---

## Publishing a new version

1. Bump `version` in `package.json`
2. Commit and push to `dev`
3. Tag and push — CI will lint, test, build, then publish to npm after `prod` environment approval:

```bash
git tag agent/v<version> && git push gh agent/v<version>
# e.g.
git tag agent/v0.2.0 && git push gh agent/v0.2.0
```