---
title: Operate the legacy bundled deployment
description: Keep an existing bundled Cal.diy or Hermes Agent deployment controlled while preparing its fleet migration
kind: how-to
audience: operator
appliesTo: "1.x"
writingStandard: "aip-docs/1.0"
lastReviewedRevision: "97be86e9efedf07ecf1783b03800f683f107fb04"
---

# Operate the legacy bundled deployment

Use this guide to keep an existing bundled Cal.diy or Hermes Agent deployment
controlled while you prepare its migration to standalone connector hosts. The
procedure freezes the compatibility baseline, preserves durable state, limits
the exposed trust boundary, verifies the running process, and defines a clear
exit from the legacy topology.

The migration-only binary is `aipd-legacy-bundled`. Its command-line parser
retains the former `aipd` command name and configuration surface for
compatibility, but its Cargo package and produced executable use the legacy
name. The package is marked `publish = false`; it is not a general release
artifact or the recommended starting point for a new deployment.

This guide reflects source revision
`97be86e9efedf07ecf1783b03800f683f107fb04`. It describes implementation
behavior, not an AIP protocol requirement or evidence that a particular
deployment has qualified for production use.

## Confirm that this guide applies

Use the legacy binary only when all of these conditions are true:

- an existing deployment already runs a product connector inside `aipd`;
- that connector is Cal.diy or Hermes Agent;
- an immediate fleet migration would create more operational risk than a
  bounded compatibility period;
- the operator can preserve the current artifact, configuration, state,
  credentials, and provider-side operation records;
- the deployment has an owner and a dated migration exit.

The public connector boundary is exact:

| Public connector | Legacy bundled path | Target path |
|---|---|---|
| Cal.diy | Temporary compatibility only | Standalone Cal.diy host |
| Hermes Agent | Temporary compatibility only | Standalone Hermes Agent host |
| Chatwoot | Not bundled | Standalone Chatwoot host |
| Dify | Not bundled | Standalone Dify host |
| CrewAI | Not bundled | Standalone CrewAI host |
| Twenty | Not bundled | Standalone Twenty host |

Do not place Chatwoot, Dify, CrewAI, or Twenty configuration in the legacy
process. Do not enable non-public development modules in a public deployment.
For a new installation, start with the product-neutral `aipd` gateway and
[deploy the connector fleet](deploy-connector-fleet.md).

## Freeze the compatibility baseline

Before changing the process, capture the exact deployment you may need to
restore. Record:

- the `aipd-legacy-bundled` artifact digest and source revision;
- the rendered command name, complete non-secret arguments, and environment
  variable names;
- the service ID, trust domain, bind address, and public base URL;
- the selected bundled connector and its endpoint or account boundary;
- the canonical manifest and MCP tool catalog;
- runtime, replay, transaction, approval, callback, and connector state;
- provider-side operation IDs for active or unresolved mutations;
- credential references, scopes, issuers, and rotation timestamps without
  copying secret values;
- ingress destinations, signature policy, and replay-store location;
- the last healthy and ready observations.

The source includes compatibility tests for the former CLI flags, normalized
default readiness, default manifest, and default MCP tool names. Those tests
protect a source baseline. They do not prove that your artifact, configuration,
database, connector endpoint, or external product is healthy.

Build only the pinned package selected for the compatibility window:

```sh
cargo build --locked --release -p aipd-legacy-bundled
```

Retain the resulting `target/release/aipd-legacy-bundled` digest with the
deployment record. Do not substitute a newly built binary during an incident
unless its source and configuration compatibility have been reviewed.

## Choose a durable state boundary

The process selects runtime storage in this order:

1. PostgreSQL when `--postgres-url` or `AIPD_POSTGRES_URL` is present;
2. durable local runtime state when `--storage-dir` or `AIPD_STORAGE_DIR` is
   present;
3. in-memory runtime state when neither is present.

Use PostgreSQL for a clustered logical deployment. Give all replicas in that
logical deployment the same runtime database and give no unrelated service
access to it. Use a local storage directory only for a deliberately
single-process deployment whose filesystem, ownership, backup, and restore
behavior are controlled.

Do not treat in-memory state as a recoverable deployment. A restart can lose
action state, idempotency reservations, transaction progress, callback
deliveries, approvals, and reconciliation evidence.

When a storage directory is configured, other local facilities can also use it,
including MCP correlation state and the default Cal.diy webhook replay file.
If PostgreSQL and a storage directory are both configured, core runtime state
uses PostgreSQL while these file-backed facilities can still depend on the
directory. Back up and restore each active store as one coordinated boundary.

Before restart or upgrade:

1. stop accepting new mutation work;
2. enumerate active, waiting, and outcome-unknown actions;
3. retain provider operation identifiers and idempotency keys;
4. create a restorable database or filesystem backup;
5. record the last completed runtime and connector observations;
6. verify the restoration procedure outside the live state path.

## Harden the external boundary

The default listener is `127.0.0.1:8080`. Keep it on loopback behind a trusted
TLS and authentication boundary, or bind externally only with an explicit
public HTTPS origin. A non-loopback listener without `--public-base-url` is
rejected outside development mode. The public URL must be an HTTPS origin
without credentials, path, query, or fragment.

Incoming native AIP envelopes require signatures by default. Configure the
expected `did:key` signer-to-principal bindings and the trusted identity and
approval-authority files for the identities the deployment accepts. If native
HTTP bearer authentication is used, prefer `--native-bearer-token-file` over
an inline token and bind the authenticated principal, tenant, and scopes
explicitly.

`--allow-insecure-development` permits unauthenticated development traffic only
on a loopback listener. It is not a production fallback. The implementation
rejects insecure development mode on a non-loopback address.

Review each optional surface separately:

| Surface | Required control |
|---|---|
| Native HTTP | Signed envelopes and trusted signers, or an explicit authenticated edge |
| MCP HTTP | OAuth introspection or a bounded local bearer policy, required scopes, and allowed browser origins |
| MCP stdio | A trusted spawning process and a private local channel |
| NATS | Authenticated server connection, bounded subjects, trust domain, queue ownership, and timeout |
| Callbacks and A2A push | Exact destination hosts, signing identity, encrypted push credentials, and HTTPS |
| Delegation | Authenticated peer identity, pinned DID, trust domain, and bounded route |

Do not enable an unused transport. Plain HTTP and private-network callback
exceptions expand the destination boundary and should remain off outside a
reviewed local topology.

## Keep secret material out of process arguments

Use owner-controlled regular files whenever the legacy interface provides a
file option. Secret-file loading rejects symbolic links, empty files, oversized
values, and group or other access on Unix. This applies to Cal.diy credentials,
native HTTP bearer tokens, NATS passwords, MCP introspection secrets, and the
connector-fleet signing seed.

Some retained compatibility options accept secret values only through an
argument or environment variable. Hermes Agent uses `--hermes-api-key` or
`AIPD_HERMES_API_KEY`; callback signing and A2A push encryption use hexadecimal
values supplied through their arguments or environment variables. These are
legacy limitations, not preferred secret-delivery interfaces.

For such a feature, inject the value through a restricted service manager or
secret provider, prevent diagnostic and environment access by unrelated users,
redact crash and support bundles, and rotate the credential after suspected
exposure. Do not place secret-bearing commands in shell history, unit files,
deployment records, or documentation examples. If the deployment cannot
control this exposure, leave the feature disabled and prioritize migration.

## Configure one bundled connector boundary

Enable only the bundled connector that the existing deployment needs. Supplying
any part of a connector's configuration makes startup validate the complete
boundary; a partial configuration fails instead of silently omitting it.

### Cal.diy

Cal.diy requires a base URL, a stable deployment-local account ID, and exactly
one authentication mode:

- a bearer-token file; or
- an OAuth client ID plus client-secret file.

Tenant-to-account routing requires both tenant-account mappings and
credential-file mappings, plus a trusted identity directory that binds the
same external accounts. Webhook replay storage requires at least one webhook
secret mapping. Without an explicit replay file, the configured storage
directory supplies the default file; without either, replay observation is
in-memory.

Preserve the existing account and tenant boundary during compatibility
operation. Do not use this page as the source for Cal.diy capability semantics
or product qualification; use the connector's local documentation.

### Hermes Agent

Hermes Agent requires one or more `ID=URL` endpoint mappings. The configured
API key is shared across those endpoints. Keep one deployment boundary only
when that shared credential and endpoint set have the same owner and trust
policy.

Operator delegation is optional and disabled unless explicitly enabled. If it
is enabled, review the model route, trusted instruction source, time and input
bounds, event limit, delegation depth, claim lifetime, cancellation grace,
capability prefixes, scope prefixes, and approval exemptions as one policy.
Do not broaden an exemption merely to preserve old behavior.

## Start under a process supervisor

Render the selected manifest before opening traffic. Clone the exact reviewed
service invocation into a non-serving validation step, add `--print-manifest`,
and retain its JSON output. The flag initializes the configured deployment,
prints the resulting manifest, and exits. Running it without the selected
connector settings prints the default manifest, not the deployment you intend
to operate.

Run the actual process through a supervisor that sets a restrictive service
user, read-only artifact, controlled working directory, bounded file
descriptors, restart policy, and access to only the required secrets and state.
Pass the reviewed non-secret settings explicitly. The following shape omits
all connector and credential values by design:

```sh
exec target/release/aipd-legacy-bundled \
  --bind 127.0.0.1:8080 \
  --public-base-url https://aip.example \
  --service-id service:aipd:legacy \
  --trust-domain example \
  --storage-dir /var/lib/aipd
```

Replace local storage with `AIPD_POSTGRES_URL` for a clustered deployment.
Configure either Cal.diy or Hermes Agent through the supervisor's protected
configuration, then compare the emitted manifest with the frozen baseline.

The process handles interrupt and termination signals through graceful HTTP
shutdown. It gives background runtime and NATS tasks a bounded period to stop.
This does not prove that an external provider mutation reached a terminal
state. Reconcile active work before stopping the old process.

## Verify before accepting traffic

Check the surfaces in this order:

1. `GET /health` confirms that the HTTP process answers; it is a liveness
   check, not a routing or connector proof.
2. `GET /ready` must return HTTP 200 and `status: ready`. Read the gateway,
   supervisor, and module details; required modules, the runtime worker, and a
   configured NATS listener participate in readiness.
3. `GET /aip/v1/manifest` must contain only the reviewed public capabilities
   for the selected configuration.
4. `GET /metrics` must be reachable through the observability boundary and
   show the daemon ready without a repeatedly failing worker.
5. Authenticated capability discovery must return only the caller's intended
   tenant-visible contract.
6. An approved non-mutating connector action must reach the expected provider
   account or endpoint and return a typed result.

Do not begin with a create, update, delete, message-send, payment, or other
mutation. A healthy process with an unexpected manifest, tenant boundary, or
provider target is not ready for traffic.

## Use the fleet bridge only for migration

The legacy binary can install the same product-neutral registry catalog and
remote handler used by `aipd`. This permits an incremental migration: one
connector can remain bundled while another tenant binding routes to an
admitted standalone host.

Fleet mode is atomic at startup. It requires the connector-registry URL, an
owner-only signing-seed file, and either an explicit connector-host allowlist
or trust in admitted registry endpoints. A partial set of fleet options is
rejected.

Keep one mutation authority for each tenant and capability. Do not expose a
bundled implementation and an enabled fleet binding for the same provider
boundary at the same time. Follow
[Migrate bundled connectors to the fleet](migrate-bundled-connectors.md) for
the drain, disabled-binding verification, cutover, and rollback sequence.

## Recover without duplicating mutations

If readiness fails, keep traffic closed and classify the failure before
restarting:

| Observation | First action |
|---|---|
| Configuration error on startup | Correct the rejected field; do not weaken the security check |
| Runtime store unavailable | Restore the same durable store before accepting work |
| Required module not ready | Verify connector endpoint, credential, account boundary, and module detail |
| Runtime worker stopped | Keep the process out of service and inspect durable work before restart |
| NATS required but listener down | Restore the authenticated NATS path or remove the reviewed dependency |
| Manifest drift | Stop rollout and restore the exact artifact and non-secret configuration |
| Provider outcome unknown | Reconcile by provider operation ID and idempotency key before retry |

Do not erase local state or replay files to make readiness pass. Do not retry an
ambiguous mutation under a new action ID. Preserve the failed process's state,
logs, metrics snapshot, manifest, and provider evidence until recovery or
rollback is complete.

## End the compatibility period

The legacy deployment is ready to retire when:

- every public connector runs in an admitted standalone host;
- the product-neutral `aipd` owns gateway, catalog, routing, and central event
  ingress responsibilities;
- provider credentials are absent from the gateway process;
- each tenant capability has exactly one enabled execution path;
- active bundled actions are terminal or reconciled;
- webhook and callback destinations point to the reviewed target boundary;
- rollback artifacts and retention obligations are satisfied;
- the legacy artifact, configuration, secrets, and state are revoked or
  archived according to policy.

After cutover, remove the compatibility binary from service definitions and
artifact promotion. Keep it only as a retained rollback artifact for the
approved window. Use [Connector host lifecycle](connector-host-lifecycle.md)
and [Connector credentials and rotation](connector-credentials-and-rotation.md)
for ongoing fleet operation.
