> For the complete documentation index, see [llms.txt](https://docs.kiloiot.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.kiloiot.io/kilo-mioty-service-center/getting-started/configuration-basics.md).

# How do I configure KiloCenter?

KiloCenter configuration files and baseline settings — config.docker.yaml, KC-Core, KC-Gateway, port mappings.

KiloCenter configuration tells its services how to reach each other, which interfaces to listen on, and which credentials and certificates to use. Review it before connecting radio equipment so traffic reaches the intended installation.

The files and addresses depend on whether you run containers or source processes on the host. Docker service names are meaningful inside the container network; host processes use their corresponding host addresses. Start with a completed [installation](/kilo-mioty-service-center/getting-started.md), then use the checks below to confirm the selected configuration and port mappings.

### What does this guide achieve?

Understand and verify baseline runtime configuration for a local deployment.

### Configuration Files

* `kilocenter-modules/config/config.docker.yaml` -- KC-Core configuration (container mode)
* `kilocenter-modules/KC-Core/config.yaml` -- KC-Core configuration (source dev mode)
* `kilocenter-modules/KC-Gateway/config.yaml` -- KC-Gateway configuration (source dev mode)
* `kilocenter-modules/docker-compose.yml` -- service definitions and port mappings

### Container Mode Defaults

In container mode, KC-Core reads `config/config.docker.yaml`. All service addresses use Docker service DNS names (e.g., `postgres`, `redis`, `mosquitto`). Environment variables in `.env` override the compose defaults.

### Source Dev Mode Defaults

`kilocenter-modules/start-dev.sh` exports environment variables and auto-detects the PostgreSQL port:

* If Docker container `kilocenter-postgres` is running, it uses port `5433`.
* Otherwise, it falls back to `5432` for a host-local PostgreSQL.

### Settings to Verify Before Testing

* **Database**: host, port, credentials (default: `localhost:5433`, user `kilocenter`, password `changeme`)
* **KC-Core health**: port `8086`
* **KC-Gateway gRPC-web**: port `9090` (the documented browser ingress; KC-Core's own gRPC-web multiplexing is off by default and is enabled with `grpc.web.enabled: true` only when a deployment talks to KC-Core directly)
* **KC-Gateway health**: port `8087`
* **BSSCI**: port `5000` in `config/config.docker.yaml`, `5005` in `KC-Core/config.yaml`, TLS certificate paths
* **SCACI**: port `5001`, TLS certificate paths
* **MQTT**: broker host, port, and credentials
* **Dashboard service status**: the rows follow the `general`, `protocol`, `grpc`, `identity` and `mqtt` settings automatically; `status.timeout` is the only status setting

### What Is the Platform Tenant?

`general.tenant_id` (default `1`) names the platform tenant. KiloCenter files operator and audit events that belong to the whole installation under it: users being created, changed or deleted, organizations and API keys being deleted, and server certificates being generated or renewed. Every edition requires a value greater than zero and refuses to start without one.

Treat the platform tenant as reserved for these events. Only administrators can read them, and members of an organization that shares the tenant see only the event categories their roles cover. See [User Roles and Permissions](/kilo-mioty-service-center/security/users-and-roles.md).

### TLS Certificate Paths

BSSCI and SCACI TLS certificate paths are configured under the `protocol` section in `KC-Core/config.yaml` (source dev) or `config/config.docker.yaml` (container):

```yaml
protocol:
  bsci_tls:
    enabled: true
    cert_file: "certificates/server.crt"
    key_file: "certificates/server.key"
    ca_file: "certificates/ca.crt"
    min_version: "1.2"
  scaci_tls:
    enabled: true
    cert_file: "certificates/server.crt"
    key_file: "certificates/server.key"
    ca_file: "certificates/ca.crt"
    min_version: "1.3"
```

Paths in the source dev config are relative to the KC-Core working directory. Container config uses absolute paths (`/app/certificates/`). Generate certificates before first start — see Docker Compose Installation.

### Downlink Lifetime

A downlink waits in the service center queue until the endpoint opens a downlink window. `protocol.downlink_expiry` bounds that wait:

```yaml
protocol:
  downlink_expiry:
    lifetime: "24h"        # how long a queued downlink waits for a window
    sweep_interval: "5s"   # how often overdue downlinks are expired
    batch_size: 100        # downlinks expired per statement
    revoke_not_held_codes: [2]  # dlDataRev refusal codes meaning "not held"
```

A downlink still waiting in the service center queue when `lifetime` has passed, or when the `expiresAt` of the MQTT command that queued it has passed, is never sent. It is marked expired and reported as `expired` to the Application Center that queued it (`dlDataRes`), on the MQTT `downlink_result` topic, and in the downlink results.

A downlink a base station already holds at that moment is not reported yet: only the station knows whether it transmitted it. KiloCenter asks the station to drop it (`dlDataRev`) and shows it as **Revoking**. It ends `expired` when the station confirms the revoke, answers that it does not hold the downlink, or reconnects with a new session that discarded it. When the station reports first that it transmitted the downlink, the result is `sent`. A connected station that has not settled the downlink is asked again once per `sweep_interval`, never while its previous `dlDataRev` still awaits an answer; a station that is offline is asked again when it reconnects. Time passing alone never ends a downlink a connected station holds.

Deleting a base station ends every downlink it held, queued, reserved or **Revoking**, as `expired` at once, whatever its deadline; none returns to the queue. A deleted station that is still powered may still transmit them, so power it off or let its queue empty before you delete it. If the deletion cannot end them, for example because the database was unavailable, the next sweep does.

A station refuses the revoke of a downlink it does not hold with a BSSCI error, whose POSIX code the specification leaves to the manufacturer. `revoke_not_held_codes` lists the codes your stations use for that answer (default `[2]`, ENOENT). Any other refusal, such as an unsupported operation, proves nothing about the downlink, which stays **Revoking** until the station reports a result, answers with a "not held" code or reconnects; a station that keeps refusing with another code keeps it **Revoking** and is asked again each `sweep_interval`. If your stations answer "not held" with another code, add the codes they use; only add a code that your stations use for "not held" alone. The same rule applies to revokes an operator or Application Center requests.

In the Helm chart the keys are `kcCore.config.protocol.downlinkExpiry.lifetime`, `sweepInterval`, `batchSize` and `revokeNotHeldCodes`.

### Application Center Session Resumption

When an Application Center loses its SCACI connection, its session stays resumable. Every uplink (`ulData`), endpoint status (`epStat`) and result of a downlink it queued (`dlDataRes`) produced while it is disconnected is kept for that session and sent when the Application Center resumes it (`snResume` true), before anything new, with operation IDs that continue from before the loss. This also holds across a KC-Core restart. An Application Center that starts a new session instead receives none of it.

An Application Center is its `acEui` within its organization. Application Centers of two organizations that use the same `acEui` each keep their own session: neither replaces or resumes the other's, and each receives only the results of the downlinks it queued.

`protocol.scaci_resume_max_pending_operations` bounds what a disconnected session holds:

```yaml
protocol:
  scaci_resume_max_pending_operations: 10000
```

When a session already holds that many operations, the next one ends its resumability: the service center discards the session, and the Application Center starts a new session when it reconnects. Uplinks and downlink results remain available through MQTT and the API. In the Helm chart the key is `kcCore.config.scaci.resumeMaxPendingOperations`.

### BSSCI External URL

`protocol.bsci_external_url` (environment variable `KILOCENTER_PROTOCOL_BSCI_EXTERNAL_URL`, default `tls://localhost:5000`) is the address base stations are told to connect to. It must be reachable from the base stations and include the host port you publish, for example `tls://sc.example.com:5000`. The web interface shows it as the **Service Center URL** when you add a base station and warns when no reachable address is configured. Its host is also added to the server certificate.

### Blueprint Registry Provider

`registry_provider` lets **Submit to Registry** on a blueprint open a pull request in a GitHub repository. It is off by default (`enabled: false`) and the **System** tab of **Blueprints** then says the registry is not configured. Set `owner`, `repo` and `token` (`KILOCENTER_REGISTRY_PROVIDER_OWNER`, `_REPO`, `_TOKEN`); `api_url`, `base_branch` (`main`), `branch_prefix` (`blueprint/`), `blueprint_path`, `file_extension` (`.json`) and `http_timeout` (30) have defaults. A GitHub App can be used instead of a token. More in [How do blueprints and decoders work?](/kilo-mioty-service-center/operations/blueprints-and-decoders.md#how-do-i-submit-a-blueprint-to-the-registry).

### Default Credentials

These defaults exist for local development convenience. Change them for any non-local deployment:

* PostgreSQL: user `kilocenter`, password `changeme`
* MQTT broker: user `admin`, password `KiloCenter`
* AVA base station factory login: `ubuntu` / `123456`

### MQTT Integration

MQTT is **disabled by default** in `KC-Core/config.yaml`:

```yaml
mqtt:
  enabled: false
  host: "localhost"
  port: 1883
  username: "admin"
  password: "KiloCenter"
```

To enable MQTT, set `enabled: true` and restart KC-Core. The Mosquitto broker must be running (it is included in the Docker Compose stack). See MQTT First Steps for integration details.

### Validation Checklist

* KC-Core starts without errors and `/health` responds on port `8086`
* KC-Gateway starts and `/health` responds on port `8087`
* **Container mode**: KC-Web loads at `http://localhost/`
* **Source dev mode**: KC-Web loads at `http://localhost:5173`
* KC-Web can reach KC-Gateway on port `9090` (gRPC-web requests succeed in browser console)


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.kiloiot.io/kilo-mioty-service-center/getting-started/configuration-basics.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
