Metadata-Version: 2.5
Name: p4n4
Version: 0.4.1
Summary: CLI tool for bootstrapping and managing P4N4 projects
Project-URL: Homepage, https://github.com/raisga/p4n4-cli
Project-URL: Documentation, https://github.com/raisga/p4n4-docs
Project-URL: Issues, https://github.com/raisga/p4n4-cli/issues
Author-email: RAISGA <jraleman@raisga.com>
License: MIT
License-File: LICENSE
Keywords: cli,docker,edge-ai,iot,scaffolding
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Build Tools
Requires-Python: >=3.11
Requires-Dist: jinja2>=3
Requires-Dist: p4n4-lib>=0.4.1
Requires-Dist: questionary>=2
Requires-Dist: rich>=13
Requires-Dist: typer[all]<0.28,>=0.12
Provides-Extra: dev
Requires-Dist: pytest-cov>=5; extra == 'dev'
Requires-Dist: pytest>=8; extra == 'dev'
Requires-Dist: ruff>=0.4; extra == 'dev'
Description-Content-Type: text/markdown

# p4n4-cli

> Command-line tool for bootstrapping and managing **P4N4** projects — Platform for Nexus Neural Network Nodes.

`p4n4` scaffolds, configures, and orchestrates the P4N4 stack: the **MING** IoT services, Edge Impulse inference, and the Gen AI layer. It wraps `docker compose` with project-aware defaults and a guided setup flow.

Part of the [p4n4](https://github.com/raisga/p4n4) platform — an EdgeAI + GenAI integration platform for IoT deployments.

---

## Table of Contents

- [Overview](#overview)
- [Repository Map](#repository-map)
- [Prerequisites](#prerequisites)
- [Installation](#installation)
- [Local Development](#local-development)
- [Quick Start](#quick-start)
- [Project Structure](#project-structure)
- [Commands](#commands)
  - [p4n4 init](#p4n4-init)
  - [p4n4 up](#p4n4-up)
  - [p4n4 down](#p4n4-down)
  - [p4n4 status](#p4n4-status)
  - [p4n4 logs](#p4n4-logs)
  - [p4n4 secret](#p4n4-secret)
  - [p4n4 validate](#p4n4-validate)
  - [p4n4 add](#p4n4-add)
  - [p4n4 remove](#p4n4-remove)
  - [p4n4 upgrade](#p4n4-upgrade)
  - [p4n4 ei](#p4n4-ei)
  - [p4n4 template](#p4n4-template)
- [Layers](#layers)
- [Configuration Reference](#configuration-reference)
- [Default Ports](#default-ports)
- [Source Repos](#source-repos)
- [Resources](#resources)
- [License](#license)

---

## Overview

P4N4 is a unified developer platform for IoT and Edge AI. It combines the **MING stack** (Mosquitto, InfluxDB, Node-RED, Grafana), **Edge Impulse** on-device ML inference, and a **Gen AI layer** (Ollama, plus optional Letta and n8n) into a single composable system.

`p4n4-cli` manages the full lifecycle — init, run, inspect, and tear down — from your terminal. Stack files are always fetched from the canonical source repos at init time, so projects are never pinned to a snapshot bundled inside the CLI.

> **Trusted networks only.** p4n4 0.2.x is meant for development and trusted local networks. Don't expose its service ports to the internet or to networks you don't control. See the [Security guide](https://github.com/raisga/p4n4-docs/blob/main/guides/security.md).

---

## Repository Map

| Repository | Description |
|------------|-------------|
| **[p4n4](https://github.com/raisga/p4n4)** | Umbrella repo — architecture, ADRs, cross-cutting docs |
| [p4n4-iot](https://github.com/raisga/p4n4-iot) | IoT stack: Mosquitto · InfluxDB · Node-RED · Grafana |
| [p4n4-ai](https://github.com/raisga/p4n4-ai) | AI stack: Ollama · Letta · n8n · System 1 · Edge Impulse / ONNX inference runner |
| [p4n4-extras](https://github.com/raisga/p4n4-extras) | Optional services outside the core (formerly p4n4-edge) |
| [p4n4-api](https://github.com/raisga/p4n4-api) | Python (FastAPI) REST API gateway (port 8000) |
| [p4n4-lib](https://github.com/raisga/p4n4-lib) | Shared Python library used by this CLI and p4n4-api (`pip install p4n4-lib`) |
| [p4n4-dashboard](https://github.com/raisga/p4n4-dashboard) | Dashboard: web service (port 8088) + desktop/mobile apps |
| **[p4n4-cli](https://github.com/raisga/p4n4-cli)** | This repo — Python CLI (`pip install p4n4`) |
| [p4n4-templates](https://github.com/raisga/p4n4-templates) | Community template registry |
| [p4n4-docs](https://github.com/raisga/p4n4-docs) | Full technical documentation site |

---

## Prerequisites

- **Python** 3.11+
- **Git** (required by `p4n4 init` to clone stack repos)
- **Docker** v24+ (with Compose v2)
- **pipx** (recommended for isolated installation)
- At least **4 GB RAM** available to Docker (8 GB recommended when running Ollama)

Install `pipx` if you don't have it:

```bash
# macOS
brew install pipx && pipx ensurepath

# Linux / WSL
python3 -m pip install --user pipx && python3 -m pipx ensurepath
```

---

## Installation

```bash
pipx install p4n4
```

Or with plain pip:

```bash
pip install p4n4
```

Verify:

```bash
p4n4 --version
```

Upgrade / uninstall:

```bash
pipx upgrade p4n4
pipx uninstall p4n4
```

---

## Local Development

```bash
# 1. Clone the repo
git clone https://github.com/raisga/p4n4-cli.git
cd p4n4-cli

# 2. Create the virtual environment
uv venv
source .venv/bin/activate

# 3. Install in editable mode with dev extras
uv pip install -e ".[dev]"

# 4. Verify
p4n4 --help

# 5. Run tests
pytest tests/ -v

# 6. Lint
ruff check .
```

---

## Quick Start

```bash
# Scaffold a new IoT project (fetches latest files from p4n4-iot)
p4n4 init my-project
cd my-project

# Validate the generated config
p4n4 validate

# Start the MING stack
p4n4 up

# Check service health
p4n4 status

# Tail logs from a specific service
p4n4 logs grafana

# Stop the stack
p4n4 down
```

**Scaffold a GenAI project instead:**

```bash
p4n4 init my-ai-project --layer ai
cd my-ai-project
p4n4 up
```

**Scaffold with all layers:**

```bash
p4n4 init my-full-project --layer all
cd my-full-project
p4n4 up
```

`all` is every core layer (iot, ai, dashboard) with the inference runner on; add the optional extras layer by name: `--layer all,extras`.

**Edge inference only** (an Edge Impulse or ONNX model on the sensor data, no LLM): `p4n4 init my-edge-project --layer iot,ai --ei-runner`, then `COMPOSE_PROFILES=ei-runner` in `ai/.env`.

**No internet access?** Point `--source-iot` / `--source-ai` / `--source-extras` at local checkouts:

```bash
p4n4 init my-project --source-iot ../p4n4-iot
p4n4 init my-ai-project --layer ai --source-ai ../p4n4-ai
p4n4 init my-project --layer iot,extras --source-iot ../p4n4-iot --source-extras ../p4n4-extras
```

Once the IoT stack is up:

| Service  | URL                   |
|----------|-----------------------|
| Grafana  | http://localhost:3000 |
| Node-RED | http://localhost:1880 |
| InfluxDB | http://localhost:8086 |

Once the AI stack is up:

| Service | URL                    |
|---------|------------------------|
| n8n     | http://localhost:5678  |
| Letta   | http://localhost:8283  |
| Ollama  | http://localhost:11434 |

---

## Project Structure

After `p4n4 init`, the project directory contains files sourced from the relevant stack repo(s). Stack files are never modified by the CLI after scaffolding.

**IoT layer (`--layer iot`, the default):**

```
my-project/
├── .p4n4.json                       # Manifest (project name, active layers, schema version)
├── .gitignore                       # Keeps .env, generated credentials and models out of git
├── .env                             # Credentials and config (auto-generated secrets)
├── docker-compose.yml               # Stack orchestration (sourced from p4n4-iot)
├── config/
│   ├── mosquitto/
│   │   ├── mosquitto.conf           # MQTT broker config
│   │   ├── passwd.example           # Auth template for production hardening
│   │   └── acl.example              # ACL template
│   ├── node-red/
│   │   ├── settings.js              # Node-RED runtime settings
│   │   └── flows/flows.json         # MQTT → InfluxDB pipeline
│   └── grafana/
│       └── provisioning/
│           ├── datasources/
│           │   └── datasources.yml  # Pre-configured InfluxDB datasources (5 buckets)
│           └── dashboards/
│               ├── dashboards.yml
│               └── json/
│                   └── iot-overview.json
└── scripts/
    ├── init-buckets.sh              # Creates additional InfluxDB buckets on first run
    ├── init-sandbox.sh
    └── selector.sh
```

**AI layer (`--layer ai`):**

```
my-ai-project/
├── .p4n4.json                       # Manifest
├── .gitignore                       # Keeps .env, generated credentials and models out of git
├── .env                             # Credentials and config (auto-generated secrets)
├── docker-compose.yml               # Stack orchestration (sourced from p4n4-ai)
├── config/
│   ├── letta/
│   │   └── letta.conf               # Letta agent server config
│   ├── n8n/
│   │   └── workflows/               # Pre-built n8n workflow definitions
│   │       ├── alert-enrichment.json
│   │       ├── device-onboarding.json
│   │       ├── incident-escalation.json
│   │       └── scheduled-digest.json
│   └── ollama/                      # Ollama model storage (populated at runtime)
└── scripts/
    ├── pull-models.sh               # Pull default Ollama models
    └── selector.sh
```

**Multi-layer (`--layer iot,ai` or `--layer all`):**

Each layer gets its own subdirectory with its own `docker-compose.yml`, `config/`,
`scripts/`, and `.env`, so the stacks run as separate Compose projects (p4n4-ai
attaches to the `p4n4-net` network that p4n4-iot creates). Shared values such as
`INFLUXDB_TOKEN` are written identically to every layer's `.env`.

```
my-project/
├── .p4n4.json                       # Manifest (lists all active layers)
├── .gitignore                       # Covers every layer's .env, credentials and models
├── iot/
│   ├── docker-compose.yml
│   ├── .env
│   ├── config/ …
│   └── scripts/ …
└── ai/
    ├── docker-compose.yml
    ├── .env
    ├── config/ …
    └── scripts/ …
```

`p4n4 up` / `down` operate on all stacks in dependency order (iot → ai → extras →
dashboard; reversed for `down`), or on one stack via `p4n4 up ai`. `p4n4 up` skips a
stack with no service enabled (an extras layer before you add one). `p4n4 logs` needs
`--stack <name>` (or `--no-follow`) in multi-layer projects.

**Extras layer (`--layer extras`, never part of `all`):**

```
my-project/extras/
├── .env                  # COMPOSE_PROFILES= (empty: nothing starts), every extra's settings
├── docker-compose.yml    # include: one line per extra (none at first)
└── _template/            # the skeleton extra: copy it to <name>/ (see the p4n4-extras README)
```

**Dashboard layer (`--layer dashboard`, included in `all`):**

```
my-project/dashboard/
├── .env                  # DASHBOARD_VERSION, DASHBOARD_PORT (8088), proxy upstreams
└── docker-compose.yml    # p4n4-dashboard: the web UI, proxying p4n4-api, Ollama and Letta
```

Only the compose file is copied: the service runs the released
`ghcr.io/raisga/p4n4-dashboard` image, and `p4n4 up` prints its URL. See the
[p4n4-dashboard README](https://github.com/raisga/p4n4-dashboard#run-as-a-service-web) for
reaching p4n4-api and Grafana from it.

---

## Commands

### `p4n4 init`

Scaffold a new P4N4 project. Clones the canonical stack repo(s) at init time so the project always starts from the latest source.

```bash
p4n4 init <project-name> [options]
```

| Flag | Description | Default |
|------|-------------|---------|
| `--layer <name>` | Layers to enable: `iot`, `ai`, `extras`, `dashboard`, `all` (every layer but `extras`; `all,extras` adds it), or comma-separated | `iot` |
| `--ei-runner` / `--no-ei-runner` | Run the ai layer's edge inference runner (adds `ei-runner` to `COMPOSE_PROFILES`) | on with `all`, else off |
| `--no-interactive` | Skip the wizard and use generated defaults | — |
| `--source-iot <path>` | Local p4n4-iot checkout (skips git clone; useful offline) | — |
| `--source-ai <path>` | Local p4n4-ai checkout (skips git clone; useful offline) | — |
| `--source-extras <path>` | Local p4n4-extras checkout (skips git clone; useful offline) | — |
| `--source-dashboard <path>` | Local p4n4-dashboard checkout (skips git clone; useful offline) | — |
| `--mqtt-remote <host[:port]>` | Bridge topics in from an external MQTT broker (needs the `iot` layer) | — |
| `--mqtt-remote-user <name>` | Username on the external broker | — |
| `--mqtt-remote-password <pw>` | Password on the external broker; prefer `P4N4_MQTT_REMOTE_PASSWORD` | — |
| `--mqtt-remote-topics <list>` | Comma-separated topic filters to pull in | `sensors/#` |
| `--mqtt-remote-tls` | Connect to the external broker over TLS (port 8883 by default) | off |
| `--mqtt-remote-ca <file>` | CA certificate for that TLS connection, copied into the project | system CAs |

The interactive wizard prompts for InfluxDB org, timezone, and admin passwords. When the `ai` layer is active it also prompts for Letta, n8n, and n8n encryption key values, and whether to run the edge inference runner. All secrets default to randomly generated values if left blank.

With the `iot` layer, the wizard also offers to pull topics from an external MQTT broker, the way `mosquitto_sub -h <host> -u <user> -P <password> -t <topic>` would. It asks for the host, credentials, topics, an optional local prefix (e.g. `remote/`) and TLS settings. The local broker then subscribes to those topics on the external one and republishes them locally, where Node-RED and the edge runner pick them up as usual. Nothing is published back. The settings land in the IoT `.env` as `MQTT_REMOTE_*` (see the [p4n4-iot README](https://github.com/raisga/p4n4-iot#external-mqtt-broker)).

```bash
# Non-interactive: pull sensors/# from a broker that requires a login
P4N4_MQTT_REMOTE_PASSWORD='s3cret' p4n4 init plant-a --no-interactive \
  --mqtt-remote broker.example.com --mqtt-remote-user plant-a --mqtt-remote-tls
```

```bash
# IoT stack, interactive wizard (default)
p4n4 init sensor-network

# GenAI stack, non-interactive
p4n4 init my-ai-project --layer ai --no-interactive

# All layers enabled
p4n4 init my-full-project --layer all

# From local checkouts (no network required)
p4n4 init sensor-network --source-iot ../p4n4-iot --no-interactive
p4n4 init my-ai-project --layer ai --source-ai ../p4n4-ai --no-interactive
```

---

### `p4n4 up`

Start the project's stacks (`docker compose up -d`), or one of them.

```bash
p4n4 up [stack] [options]
```

| Argument / flag | Description |
|------|-------------|
| `stack` | Start one stack only: `iot`, `ai`, `extras` or `dashboard` |
| `--build` | Rebuild images before starting |
| `--pull` | Pull the latest images before starting |
| `--no-detach` | Run in the foreground |
| `--emu <profile>` | Run under [p4n4-emu](https://github.com/raisga/p4n4-emu) with a hardware profile (`rpi4`, `rpi5`, `nuc`, `mcu-class`): the device's CPU, memory and disk limits, and its architecture through QEMU |

```bash
p4n4 up
p4n4 up --pull
p4n4 up ai
p4n4 up --emu rpi5
```

`--emu` runs `p4n4-emu up --profile <profile>` for the stacks, so `p4n4-emu` must be on
`PATH` (`uv tool install git+https://github.com/raisga/p4n4-emu`). `p4n4 down` notices
stacks started that way and stops them through `p4n4-emu down`, which also removes the
resource-limit overlay and the sensor simulator.

---

### `p4n4 down`

Stop all running services.

```bash
p4n4 down [options]
```

| Flag | Description |
|------|-------------|
| `--volumes` | Also remove persistent data volumes _(destructive — prompts for confirmation)_ |
| `--all` | Stop every p4n4 project on this host, from any directory _(lists them and prompts first)_ |

```bash
p4n4 down

# Full teardown — deletes all stored data
p4n4 down --volumes

# Free the host for another project
p4n4 down --all
```

p4n4 stacks use fixed container names (`p4n4-influxdb`, …) and host ports, so only one
project runs on a host at a time. `p4n4 up` checks for that before starting anything and
names the project holding the containers; `p4n4 down --all` stops it, wherever it lives.

---

### `p4n4 status`

Print a service status table.

```bash
p4n4 status
```

```
       Stack status — my-project
┏━━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━┓
┃ Service   ┃ Status  ┃ Health  ┃ Ports                 ┃
┡━━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━┩
│ mqtt      │ running │ healthy │ 1883→1883/tcp          │
│ influxdb  │ running │ healthy │ 8086→8086/tcp          │
│ node-red  │ running │ healthy │ 1880→1880/tcp          │
│ grafana   │ running │ healthy │ 3000→3000/tcp          │
└───────────┴─────────┴─────────┴───────────────────────┘
```

---

### `p4n4 logs`

Stream logs from all services or a specific one.

```bash
p4n4 logs [service] [options]
```

| Flag | Description | Default |
|------|-------------|---------|
| `--tail <n>` | Lines from end of existing logs | `100` |
| `--no-follow` | Print once and exit | — |

```bash
p4n4 logs
p4n4 logs node-red
p4n4 logs influxdb --tail 50 --no-follow
```

---

### `p4n4 secret`

Rotate secrets in `.env` with new randomly generated values. Prompts for confirmation before writing. Rotates whichever of the following keys are present, which their services read at every start:

- **IoT layer:** `NODE_RED_PASSWORD`
- **AI layer:** `LETTA_SERVER_PASSWORD`

It doesn't rotate `INFLUXDB_PASSWORD`, `INFLUXDB_TOKEN`, `GRAFANA_PASSWORD` or `N8N_ENCRYPTION_KEY`, and says so. Their services keep the value from their first setup, so a new value in `.env` alone would lock every client out of InfluxDB and Grafana, and stop n8n from starting. Change those in the service itself (the [Security guide](https://github.com/raisga/p4n4-docs/blob/main/guides/security.md#secret-rotation) explains how). `p4n4 secret show` lists all of them, masked.

```bash
p4n4 secret
```

Run `p4n4 down && p4n4 up` after rotating secrets to apply them.

`p4n4 secret show` also lists `MQTT_REMOTE_PASSWORD` when an external broker is configured, fully masked. Rotation never touches it: the external broker issued it, so a new value would stop the bridge from logging in.

---

### `p4n4 validate`

Check that `.p4n4.json`, `.env`, and all required config files are present and valid for the active layers.

```bash
p4n4 validate
```

---

### `p4n4 migrate`

Move a legacy layer into the layer that replaced it. The only one is `edge`: p4n4-edge's inference runner is now the ai layer's `ei-runner` profile, and p4n4-edge became p4n4-extras.

```bash
p4n4 down edge
p4n4 migrate edge [--source-ai <path>] [--yes]
p4n4 validate && p4n4 up ai
```

- An edge layer that runs the inference runner moves into the **ai** layer: its service (spliced into `ai/docker-compose.yml`, which keeps its comments), `runner/`, the models in `edge-impulse/models/` and `onnx/models/`, and its `.env` values; `ei-runner` joins `COMPOSE_PROFILES`. A project without an ai layer gets one, scaffolded from p4n4-ai (or `--source-ai`) with only the runner enabled. A project left with one layer is flattened to the root, as `p4n4 init` lays it out.
- An edge layer that runs other services (a template's own, such as video) becomes the **extras** layer, keeping its Compose project name so its volumes stay.
- Nothing is deleted: what the move replaces stays in `edge.pre-migrate/` until you remove it. Keys both `.env` files set differently keep the ai layer's value and are listed.

Until it's migrated, an `edge` layer keeps working, and every command prints a reminder.

---

### `p4n4 add`

Add a layer or service to an existing project.

```bash
p4n4 add <component>
```

> Not yet implemented.

---

### `p4n4 remove`

Remove a layer or service from an existing project.

```bash
p4n4 remove <component>
```

> Not yet implemented.

---

### `p4n4 upgrade`

Pull the latest Docker images for all active services.

```bash
p4n4 upgrade
```

> Not yet implemented.

---

### `p4n4 ei`

Manage Edge Impulse model deployment and inference (the ai layer's `ei-runner` service).

```bash
p4n4 ei deploy
p4n4 ei run
```

| Subcommand | Description |
|------------|-------------|
| `deploy` | Deploy a trained Edge Impulse model to the edge device |
| `run` | Run inference and publish results over MQTT |

> Not yet implemented.

---

### `p4n4 template`

Browse and apply community project templates.

```bash
p4n4 template search [query]
p4n4 template apply <name>
```

> Not yet implemented.

---

## Layers

P4N4 is organized into two core layers, iot and ai, plus the dashboard and the optional extras.

### IoT — MING Stack

| Service | Role |
|---------|------|
| **Mosquitto** | MQTT broker — central message bus |
| **InfluxDB** | Time-series database (5 pre-configured buckets) |
| **Node-RED** | Flow engine for MQTT routing and pipeline logic |
| **Grafana** | Real-time dashboarding and alerting |

### AI

| Service | Role | Starts by default |
|---------|------|-------------------|
| **Ollama** | Local LLM runtime for open-weight models | Yes |
| **Letta** | Stateful AI agent framework with long-term memory | No |
| **n8n** | Workflow automation bridging IoT and AI layers | No |
| **System 1** | Fast typed decisions (laya), advisory | No |
| **ei-runner** | Edge Impulse / ONNX inference on the sensor data; publishes results over MQTT | With `--layer all` or `--ei-runner` |

Every service sits in a Compose profile of its own name, and `COMPOSE_PROFILES` in the
layer's `.env` lists the ones that start. `p4n4 init` writes the stack's default
(`COMPOSE_PROFILES=ollama`, plus `ei-runner` with `--layer all` or `--ei-runner`) and still
generates Letta's and n8n's secrets. To run them, set
`COMPOSE_PROFILES=ollama,letta,n8n` in `ai/.env` (`.env` in a single-layer project) and run
`p4n4 up ai`.

### Extras (optional)

Services outside the core, from [p4n4-extras](https://github.com/raisga/p4n4-extras): none starts until its layer's `COMPOSE_PROFILES` lists it, and no core service depends on one. Add the layer by name (`--layer iot,ai,extras` or `--layer all,extras`).

All stacks communicate over a shared `p4n4-net` Docker bridge network owned by `p4n4-iot`. The CLI handles network creation and stack ordering automatically.

---

## Configuration Reference

`.env` is generated by `p4n4 init` with random secrets. Use `p4n4 secret` to rotate credentials at any time.

**IoT layer:**

```bash
TZ=UTC

# InfluxDB
INFLUXDB_USERNAME=admin
INFLUXDB_PASSWORD=<generated>
INFLUXDB_ORG=ming
INFLUXDB_TOKEN=<generated>
INFLUXDB_BUCKET=raw_telemetry
INFLUXDB_BUCKET_PROCESSED=processed_metrics
INFLUXDB_BUCKET_AI_EVENTS=ai_events
INFLUXDB_BUCKET_HEALTH=system_health
INFLUXDB_SANDBOX_BUCKET=sandbox
INFLUXDB_SANDBOX_RETENTION=30d

# Grafana
GRAFANA_USER=admin
GRAFANA_PASSWORD=<generated>

# Node-RED editor login
NODE_RED_USER=admin
NODE_RED_PASSWORD=<generated>
```

**AI layer:**

```bash
# Letta
LETTA_SERVER_PASSWORD=<generated>

# n8n
N8N_BASIC_AUTH_USER=admin
N8N_BASIC_AUTH_PASSWORD=<generated>
N8N_ENCRYPTION_KEY=<generated>   # 32+ characters
N8N_HOST=localhost

# Shared InfluxDB (must match p4n4-iot values when used alongside it)
INFLUXDB_TOKEN=<generated>
INFLUXDB_ORG=ming
INFLUXDB_BUCKET=raw_telemetry
```

---

## Default Ports

| Service              | Port                              | Stack |
|----------------------|-----------------------------------|-------|
| MQTT (Mosquitto)     | `1883` (MQTT), `9001` (WebSocket) | iot   |
| InfluxDB             | `8086`                            | iot   |
| Node-RED             | `1880`                            | iot   |
| Grafana              | `3000`                            | iot   |
| n8n                  | `5678`                            | ai    |
| Letta                | `8283`                            | ai    |
| Ollama               | `11434`                           | ai    |
| Edge Impulse Runner  | `8080` (health endpoint)          | ai (`ei-runner` profile) |
| **p4n4 REST API**    | **`8000`**                        | **api** |

---

## Source Repos

Stack files are fetched from these repos at `p4n4 init` time. URLs are defined in `p4n4_lib/sources.yaml` (shipped with [`p4n4-lib`](https://github.com/raisga/p4n4-lib)) and can be overridden per-layer via `--source-iot` / `--source-ai` / `--source-extras`, or by editing the file to point at a fork or mirror.

| Layer     | Repo |
|-----------|------|
| IoT       | https://github.com/raisga/p4n4-iot |
| AI        | https://github.com/raisga/p4n4-ai |
| Extras    | https://github.com/raisga/p4n4-extras |
| Templates | https://github.com/raisga/p4n4-templates |

---

## Resources

- [p4n4.com](https://p4n4.com) — Platform overview
- [Full documentation](https://raisga.github.io/p4n4-docs) — MkDocs reference site
- [p4n4-iot](https://github.com/raisga/p4n4-iot) — MING stack source
- [p4n4-ai](https://github.com/raisga/p4n4-ai) — AI stack source (GenAI and the Edge Impulse / ONNX runner)
- [p4n4-extras](https://github.com/raisga/p4n4-extras) — optional services (formerly p4n4-edge)
- [p4n4-templates](https://github.com/raisga/p4n4-templates) — Community template registry
- [MING Stack Tutorial](https://github.com/ArthurKretzer/tutorial-p4n4-stack) — IIoT data stack tutorial (XIV SBESC, 2024)
- [MING Stack with Edge Impulse](https://www.edgeimpulse.com/blog/accelerate-edge-ai-application-development-with-the-p4n4-stack-edge-impulse/) — Integration guide
- [Edge Impulse Linux SDK (Python)](https://github.com/edgeimpulse/linux-sdk-python)
- [Eclipse Mosquitto](https://mosquitto.org/) · [InfluxDB](https://docs.influxdata.com/) · [Node-RED](https://nodered.org/docs/) · [Grafana](https://grafana.com/docs/)
- [Ollama](https://github.com/ollama/ollama) · [Letta](https://docs.letta.com/) · [n8n](https://docs.n8n.io/)

---

## License

This project is licensed under the [MIT License](LICENSE).
