greentic-operator 0.4.40

Greentic operator CLI for local dev and demo orchestration.
# greentic-operator

Greentic Operator orchestrates a project directory for demos and local development.
It manages tenants/teams, access mapping (.gmap), pack/provider discovery, resolved manifests, and starting local runtime services.

## Quickstart (demo)

```bash
mkdir my-demo && cd my-demo
gtc op demo new demo-bundle
# drop provider packs into providers/messaging/ and providers/events/
# drop packs into packs/
gtc op demo setup --bundle demo-bundle --tenant default --team default
gtc op demo build --bundle demo-bundle --tenant default --team default
gtc op demo start --bundle demo-bundle --tenant default --team default
```

`demo start` is the canonical, long-running invocation: it boots the demo services in the foreground and waits for **Ctrl+C** to trigger a clean shutdown sequence. Press **Ctrl+C** in the terminal running the command to stop the services.

Lifecycle ownership note: `greentic-operator demo start|up|stop|restart` delegates runtime lifecycle execution to `greentic-start`. Wizard, setup, and bundle UX remain in `greentic-operator`.

Access mapping (.gmap)

Rules are line-oriented:

<path> = <policy>

Paths:

_ default

pack_id, pack_id/_, pack_id/flow_id, pack_id/flow_id/node_id

Policies (MVP):

public

forbidden

Team rules override tenant rules.

Demo bundles

gtc op demo build --out demo-bundle --tenant tenant1 --team team1
gtc op demo start --bundle demo-bundle --tenant tenant1 --team team1
Note: demo bundles require CBOR-only packs (`manifest.cbor`). Rebuild packs with `greentic-pack build` (avoid `--dev`).

### allow/forbid commands

There are two sets of gmap editing helpers:

- `gtc op demo allow/forbid` is meant for portable bundles. Supply `--bundle <DIR>` plus `--tenant`/`--team` and pass the same `PACK[/FLOW[/NODE]]` path. The command rewrites the bundle’s gmap, reruns the resolver, and copies the updated `state/resolved/<tenant>[.<team>].yaml` into `resolved/`, so `demo start` immediately sees the change.

Paths must contain at most three segments. Passing `PACK/FLOW/NODE/EXTRA` (or relative paths with more than three parts) will trigger the “too many segments” error you saw. Stick to the `pack`, `pack/flow`, or `pack/flow/node` forms.

Demo send (generic)

gtc op demo send --bundle demo-bundle --provider telegram --print-required-args
gtc op demo send --bundle demo-bundle --provider telegram --text "hi" --arg chat_id=123
gtc op demo send --bundle demo-bundle --provider telegram --card cards/welcome.json --arg chat_id=123

Demo new (bundle scaffold)

gtc op demo new demo-bundle
gtc op demo new demo-bundle --out /tmp

Creates the directory layout plus minimal metadata (`greentic.demo.yaml`, `tenants/default/tenant.gmap`, `providers/*`, `state`, `resolved`, `logs`, etc.) so you can start adding packs and tenant definitions before running `demo setup`/`demo build`.

Demo receive (incoming)

Terminal A: `gtc op demo receive --bundle demo-bundle`
Terminal B: `gtc op demo send --bundle demo-bundle --provider telegram --text "hi" --arg chat_id=123`

`demo receive` listens for the bundle's messaging ingress subjects, streams each message to stdout, and appends a JSON line to `incoming.log`. Use `--provider` to focus on a single provider or `--all`/default to watch every enabled messaging pack.

### demo ingress (synthetic HTTP)

`gtc op demo ingress` lets you exercise the universal HTTP ingress and operator outbound pipeline without running a full HTTP gateway. It constructs an `HttpInV1` body, invokes the provider `ingest_http` flow, prints the HTTP response plus any `ChannelMessageEnvelope` events, and (with `--end-to-end`) pushes the events through the app + render/encode/send flow.

Key flags:

- `--provider <name>` (required): provider pack or ID (slack, telegram, teams, webex, whatsapp, webchat, email, dummy, etc.).
- `--bundle <dir>` (required): demo bundle directory containing the provider packs.
- `--path <path>`: overrides the default `/ingress/<provider>/webhook` path (or `/ingress/<provider>/<binding_id>` when `--binding-id` is set).
- `--method <GET|POST>`: choose the HTTP method (default `POST`).
- `--body`, `--body-json`, `--body-raw`: body source (only one allowed).
- `--binding-id <id>`: populate the binding ID/route for Telegram-style callbacks.
- `--print <http|events|all>`: control printed output (defaults to `all`).
- `--end-to-end`: invoke `render_plan`, `encode`, and `send_payload` for each event.
- `--send`/`--dry-run`: choose whether `send_payload` actually fires (default dry-run when end-to-end).
- `--retries <n>`: cap the retry attempts when `send_payload` returns `node-error`.
- `--dlq-tail`: prints the shared `dlq.log` path (same file the runtime uses).

Use `--tenant`/`--team`/`--correlation-id` to simulate the context headers that would arrive via a real gateway. Add `--app-pack` to target a custom app pack override instead of the demo’s default selection.

## Domain auto-discovery

Domains are enabled automatically when provider packs exist:

- messaging: `providers/messaging/*.gtpack`
- events: `providers/events/*.gtpack`

You can override per-domain behavior in `greentic.yaml`:

```yaml
services:
  messaging:
    enabled: auto   # auto|true|false
  events:
    enabled: auto   # auto|true|false
```

## Capability bootstrap checks (setup)

`demo setup` runs a capability bootstrap report before provider flow execution. Lifecycle execution itself is delegated to `greentic-start`.

- Required (domain-driven): messaging provider, events provider, and secrets store capabilities.
- Recommended (when messaging/events are enabled): OAuth broker and MCP exec capabilities.
- Migration compatibility: if secrets capability is not yet explicitly offered but secrets provider packs are present, Operator logs legacy fallback usage instead of failing required checks.

Details: `docs/OPERATOR_MESSAGING_PROVIDER_INTEGRATION_GUIDE.md` (section "Capability bootstrap checks in setup").

## Wizard Playbooks

- `docs/wizard.md`
- `docs/creating-bundles-from-oci-for-humans.md`
- `docs/creating-bundles-from-oci-for-codex.md`

## Dev/demo dependency mode

Dev/demo uses local path dependencies for greentic-* crates with `version = "0.4"` and
`path = "../<repo>"`. Publishing (future) requires stripping path deps and relying on
registry-only versions.

## Legacy commands

Everything under `greentic-operator dev …` is legacy.

- `greentic-operator dev on|off|status`
- `greentic-operator dev up|down|embedded`
- `greentic-operator dev logs|svc-status`

Other dev subcommands remain callable but are hidden from `--help`.

## Local dev binaries

When iterating in a workspace/monorepo, you can resolve binaries from local build outputs
instead of relying on `cargo binstall` or `$PATH`:

```bash
greentic-operator dev on --root /projects/ai/greenticai --profile debug
greentic-operator dev detect --root /projects/ai/greenticai --profile debug --dry-run
gtc op demo start --bundle demo-bundle --tenant default --team default
gtc op demo doctor
```

Config (greentic.yaml) supports dev mode defaults and explicit binary overrides:

```yaml
dev:
  mode: auto
  root: /projects/ai/greenticai
  profile: debug
  target_dir: null
  repo_map:
    greentic-pack: greentic-pack
    greentic-secrets: greentic-secrets
binaries:
  greentic-pack: /custom/bin/greentic-pack
```

Resolution order (hybrid dev-mode):
1) Explicit config path (binaries map or command path).
2) Dev-mode repo_map override under `dev.root` (if enabled).
3) Fallbacks (`./bin`, `./target/*`, then `$PATH`).

Global dev-mode settings are stored in `~/.config/greentic/operator/settings.yaml` (platform-
appropriate equivalents on macOS/Windows). Use `greentic-operator dev status` to view them and
`greentic-operator dev off` to disable dev mode globally.

## Demo service config

`gtc op demo start` delegates lifecycle execution to `greentic-start`, which reads the `services` section of `greentic.yaml` / `greentic.demo.yaml` to decide which gateway/egress/subscriptions components to launch. By default, demo start does **not** spawn local NATS (`--nats=off`), but you can opt into the legacy GSM NATS stack with `--nats=on` (this prints a warning) or attach to an external NATS server via `--nats=external --nats-url <URL>`.

When the demo bundle exposes a gateway host/port (either via `greentic.demo.yaml` or `greentic.yaml`), `greentic-start` runs the HTTP ingress and embedded lifecycle/runtime path. `demo start` also logs `embedded runner mode; gateway/egress disabled` when it avoids launching the legacy GSM services, so the CLI stays on the embedded path unless `--nats=on` is explicitly requested.

## Webhook tunneling

`demo start` can automatically spawn a public tunnel through `greentic-start` so that external services (Telegram, Slack, Teams, etc.) can deliver webhooks to your local machine. Two tunnel backends are supported: **Cloudflare Tunnel** (default) and **ngrok**.

| Flag | Default | Description |
|------|---------|-------------|
| `--cloudflared <on\|off>` | `on` | Start a Cloudflare quick tunnel (`*.trycloudflare.com`). |
| `--cloudflared-binary <PATH>` || Explicit path to the `cloudflared` binary. |
| `--ngrok <on\|off>` | `off` | Start an ngrok tunnel (`*.ngrok-free.app`). |
| `--ngrok-binary <PATH>` || Explicit path to the `ngrok` binary. |

Only one tunnel should be active at a time. To use ngrok instead of cloudflared:

```bash
gtc op demo start --bundle demo-bundle \
  --cloudflared off --ngrok on
```

The discovered public URL is written to `state/runtime/<tenant>/<team>/public_base_url.txt`. Both backends can be restarted via `--restart ngrok` or `--restart cloudflared`.

Binary resolution follows the standard order: explicit `--*-binary` flag, `GREENTIC_<NAME>` env var, `<bundle>/bin/`, `<bundle>/target/{debug,release}/`, then `$PATH`.

## Demo subscriptions mode

`gtc op demo start` defaults to the embedded universal subscriptions scheduler provided by `greentic-start`. Use `services.subscriptions.mode` in `greentic.yaml` to switch between the legacy GSM binary and the provider-op driven implementation:

```yaml
services:
  subscriptions:
    mode: universal_ops        # universal_ops | legacy_gsm
    universal:
      renew_interval_seconds: 60
      renew_skew_minutes: 10
      desired:
        - provider: email
          resource: "/me/mailFolders('Inbox')/messages"
          change_types: ["created", "deleted"]
          notification_url: "https://example.com/ingress/email/subscriptions/<binding_id>"
          client_state: "demo-${provider}"
          user:
            user_id: "alice@example.com"
            token_key: secrets://demo/default/messaging/alice_refresh_token
```

The universal scheduler calls the provider `subscription_*` ops (using the shared `messaging_universal_dto` contract) and persists binding metadata under `state/subscriptions/<provider>/<tenant>/<team>/<binding_id>.json`. The stored `AuthUserRefV1` is reused when renewing or deleting subscriptions, which keeps delegated email/Teams contexts intact.

Use `gtc op demo subscriptions` to manage bindings manually:

- `demo subscriptions ensure` invokes `subscription_ensure`, stores the binding, and prints its path.
- `demo subscriptions status` lists persisted bindings plus expiry timestamps.
- `demo subscriptions renew` runs the scheduler (either all due bindings or a single `--binding-id`) and re-writes state.
- `demo subscriptions delete` calls `subscription_delete` and removes the stored file.

These commands are handy for smoke testing provider packs and delegated scenarios without running a full demo stack.

Snapshot `docs/demo-universal-subscriptions.yaml` contains a ready-to-use `greentic.demo.yaml` snippet you can drop into a bundle before running `demo start --subscriptions-mode universal_ops`.