Skip to content

Commit 496229d

Browse files
committed
Update nxt-sts docs :: Add DO app platform instructions for nxt-sts
1 parent 75956e8 commit 496229d

6 files changed

Lines changed: 324 additions & 39 deletions

File tree

docs/ops/repo-doc-sync-state.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,8 @@ nxt-sts:
5858
standard: single-repo
5959
doc_mode: maintenance
6060
status: standard-compliant
61-
last_synced_sha: 3d12f413cc5363ca208505e8872ffd08e6ff14af
62-
last_synced_at: 2026-05-06
61+
last_synced_sha: 86f7581820fba938b7523632d10b93f3d713d714
62+
last_synced_at: 2026-07-07
6363

6464
nxt-topup:
6565
repo: nxtgrid/nxt-topup

docs/repositories/nxt-sts.mdx

Lines changed: 55 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -6,73 +6,95 @@ title: nxt-sts
66

77
## Purpose
88

9-
`nxt-sts` is a Spring Boot microservice that issues prepaid metering tokens compliant with IEC 62055-41 (STS). It is used as a backend token-generation service for prepaid utility workflows.
9+
`nxt-sts` is a stateless Spring Boot microservice that generates prepaid metering tokens compliant with IEC 62055-41 (STS). It exposes a small REST API consumed by backend integration layers in prepaid utility workflows.
1010

1111
## Scope
1212

1313
- In scope:
14-
- STS token issuance via HTTP endpoint for supported token classes.
14+
- STS token issuance via `POST /token` for four supported token types.
1515
- Decoder-key based token generation using the Standard Transfer Algorithm (STA/EA07).
16-
- Mapping token-request payloads to token-generator implementations.
16+
- Strategy-based dispatch from HTTP requests to token generator implementations.
17+
- Self-describing API surface (OpenAPI/Swagger) and health endpoint for orchestration.
1718
- Out of scope:
1819
- Customer payment UX and transaction orchestration (handled by other services/apps).
1920
- Meter communication and post-issuance delivery channels.
21+
- Token persistence, replay tracking, or meter state management (each request is a pure function).
2022

2123
## What this service does in production
2224

23-
- Accepts token-issue requests at `/token`.
24-
- Builds STS domain primitives (`TokenIdentifier`, `RandomNo`, `DecoderKey`, and type-specific payload fields).
25-
- Routes to generator implementations for supported token types and returns generated token numbers.
26-
- Exposes a lightweight API surface intended to be consumed by backend integration layers.
25+
- Accepts token-issue requests at `POST /token` and returns a 20-digit IEC 62055-41 token string.
26+
- Dispatches requests to a matching `TokenStrategy` implementation (`TOP_UP`, `CLEAR_CREDIT`, `CLEAR_TAMPER`, `SET_POWER_LIMIT`).
27+
- Validates input before generation (token type, required fields, `randomNumber` range 0–15, decoder key format, ISO 8601 issue date).
28+
- Returns structured JSON error responses with HTTP 400/500 status codes on failure.
29+
- Exposes operational endpoints: `GET /` (service index), `GET /swagger` (Swagger UI), `GET /actuator/health`.
2730

2831
## Primary workflows
2932

3033
- Top-up token workflow: client submits `type=TOP_UP` with `kwh`, issue date, random number, and decoder key; service returns a generated 20-digit token.
3134
- Clear operation workflow: client submits `CLEAR_CREDIT` or `CLEAR_TAMPER`; service builds class-2 token structures and returns token.
3235
- Power-limit workflow: client submits `SET_POWER_LIMIT` with `powerLimit`; service generates corresponding class-2 control token.
36+
- Integration discovery workflow: client calls `GET /` or opens `/swagger` to inspect available endpoints and request schema before wiring backend callers.
3337

3438
## Setup and run
3539

3640
- Repository: [github.com/nxtgrid/nxt-sts](https://github.com/nxtgrid/nxt-sts)
37-
- Build: `mvn clean install -DskipTests` (produces `target/jambu-1.0-SNAPSHOT.jar`).
38-
- Run locally: `java -jar target/jambu-1.0-SNAPSHOT.jar` (default port 8080).
39-
- Production process is configured for Gunicorn-equivalent Java path via Spring Boot JAR runtime and Docker image execution.
41+
- Prerequisites: Java 17+ (local Maven not required — repository includes `./mvnw`).
42+
- Build: `./mvnw clean package -DskipTests` (produces `target/nxt-sts-*.jar`).
43+
- Run locally (dev): `./mvnw spring-boot:run` (default port 8080).
44+
- Run packaged JAR: `java -jar target/nxt-sts-*.jar` (override port with `--server.port=8084`).
45+
- Docker: `docker build -t nxt-sts . && docker run -p 8080:8080 nxt-sts` (multi-stage Dockerfile; no pre-built JAR required).
46+
- Released container image: `ghcr.io/nxtgrid/nxt-sts:latest` (published on version tags such as `v1.0.0`).
47+
48+
## Deployment
49+
50+
Platform-specific deployment guides live under this repository section:
51+
52+
- [DigitalOcean App Platform](./nxt-sts/digital-ocean) — deploy from GHCR or build from the GitHub repository.
4053

4154
## APIs and interfaces
4255

43-
- `POST /token` is the primary service endpoint (implemented in `MyApplication`).
44-
- Request contract (`RequestData`) includes:
56+
- `POST /token` primary token generation endpoint (`TokenController`).
57+
- Request contract (`TokenRequest`):
4558
- `type` (`TOP_UP`, `CLEAR_CREDIT`, `CLEAR_TAMPER`, `SET_POWER_LIMIT`)
46-
- `issueDate`
47-
- `randomNumber`
48-
- `decoderKey`
49-
- `kwh` (for top-up)
50-
- `powerLimit` (for set-power-limit)
51-
- Response contract returns generated token string as `{ "token": "<token-number>" }`.
52-
- Decoder key is converted from hex string into reversed byte array before token generation.
59+
- `issueDate` (ISO 8601, e.g. `2024-03-15T10:30:00`)
60+
- `randomNumber` (integer 0–15; STS 4-bit RND field)
61+
- `decoderKey` (16-character hex string)
62+
- `kwh` (required for `TOP_UP`)
63+
- `powerLimit` (required for `SET_POWER_LIMIT`)
64+
- Response contract: `{ "token": "<20-digit-token>" }`.
65+
- Error contract: structured JSON with HTTP 400 (validation/unknown type) or 500 (unexpected generation failure).
66+
- Canonical API reference: Swagger UI at `/swagger` after starting the service.
5367

5468
## Integrations and dependencies
5569

56-
- Core framework: Spring Boot 3.4 web stack.
57-
- Cryptographic/token stack: Bouncy Castle + STS token domain/generator implementation classes.
58-
- Time/date parsing: Joda-Time for request issue-date handling.
59-
- Optional/auxiliary dependencies present for Redis and Thrift-based integration paths.
60-
- Service lineage: derivative work from `NectarAPI/tokens-service` (documented in `NOTICE`).
70+
- Core framework: Spring Boot 3.4 (web, validation, actuator).
71+
- Cryptographic/token stack: Bouncy Castle + STS domain/generator classes under `co.nxtgrid.token.*`.
72+
- Time/date parsing: Joda-Time for IEC 62055-41 date handling.
73+
- API documentation: springdoc-openapi (Swagger UI + OpenAPI JSON).
74+
- CI: GitHub Actions runs `./mvnw verify` on push/PR to `main`.
75+
- Container releases: tagged versions publish to GHCR via `.github/workflows/release.yml`.
76+
- Service lineage: derivative work from `NectarAPI/tokens-service` (documented in `NOTICE`, AGPL-3.0).
6177

6278
## Operations notes
6379

64-
- Default service port is 8080 with runtime override support (`--server.port`).
65-
- Build artifact handling is explicit in `.gitignore` to keep the required JAR path available for deployment workflows.
80+
- Default HTTP port is 8080; override via `SERVER_PORT` env var or `--server.port`.
81+
- Health check path for load balancers and orchestrators: `/actuator/health` (returns `{"status":"UP"}`).
82+
- Decoder keys are meter-specific secrets — transmit only over HTTPS; do not log or persist in plaintext.
6683
- Key failure modes to check first:
67-
- invalid token type or missing type-specific fields (`kwh`/`powerLimit`),
68-
- malformed decoder key causing conversion/generation failures,
69-
- date format parsing failures for `issueDate`.
84+
- invalid or missing token type / type-specific fields (`kwh`, `powerLimit`),
85+
- `randomNumber` outside 0–15 (STS protocol constraint, not an arbitrary API limit),
86+
- malformed `decoderKey` (must be exactly 16 hex characters),
87+
- unparseable `issueDate` format.
7088

7189
## Source of truth
7290

7391
- Repository: [github.com/nxtgrid/nxt-sts](https://github.com/nxtgrid/nxt-sts)
74-
- Endpoint/controller implementation: `src/main/java/co/nxtgrid/MyApplication.java`
75-
- Request payload model: `src/main/java/co/nxtgrid/RequestData.java`
92+
- Application bootstrap: `src/main/java/co/nxtgrid/StsApplication.java`
93+
- Token endpoint: `src/main/java/co/nxtgrid/api/TokenController.java`
94+
- Request/response models: `src/main/java/co/nxtgrid/api/TokenRequest.java`, `TokenResponse.java`
95+
- Strategy implementations: `src/main/java/co/nxtgrid/strategy/`
96+
- Runtime config: `src/main/resources/application.properties`
7697
- Build/runtime dependencies: `pom.xml`
77-
- Container/runtime packaging: `Dockerfile` and `.gitignore`
78-
- Service behavior and token-type docs: `README.md`
98+
- Container packaging: `Dockerfile`
99+
- Token capability matrix: `docs/capabilities.md`
100+
- Operator runbook (build, test, Docker, API): `README.md`
Lines changed: 256 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,256 @@
1+
---
2+
title: DigitalOcean App Platform
3+
---
4+
5+
# Deploy nxt-sts on DigitalOcean App Platform
6+
7+
This guide deploys `nxt-sts` on [DigitalOcean App Platform](https://docs.digitalocean.com/products/app-platform/) as a web service. Choose one of two source options:
8+
9+
1. **Pre-built GHCR image** — pull a released container from GitHub Container Registry.
10+
2. **GitHub repository build** — App Platform builds the image from the repository `Dockerfile` on each deploy.
11+
12+
Both paths produce the same runtime: a stateless Spring Boot service listening on port **8080** with health checks at `/actuator/health`.
13+
14+
## Prerequisites
15+
16+
- DigitalOcean account with App Platform access.
17+
- For GHCR deploy: a published image at `ghcr.io/nxtgrid/nxt-sts` (created when a `v*.*.*` tag is pushed to the repository).
18+
- For GitHub build: DigitalOcean authorized to access the `nxtgrid/nxt-sts` repository.
19+
- If the GHCR package is private: a GitHub personal access token with `read:packages` scope, added as a container registry credential in App Platform.
20+
21+
## Shared runtime settings
22+
23+
Configure the HTTP port during app creation. Health checks are configured separately after the app exists — see [Configure health checks](#configure-health-checks-settings-tab) below.
24+
25+
| Setting | Value |
26+
|---|---|
27+
| Component type | Web Service |
28+
| HTTP port | `8080` |
29+
30+
Optional environment variables (safe defaults are committed in the repository):
31+
32+
| Variable | Purpose | Default |
33+
|---|---|---|
34+
| `SERVER_PORT` | HTTP listen port | `8080` |
35+
| `SPRING_APPLICATION_NAME` | Service name in logs/actuator | `nxt-sts` |
36+
37+
No database, Redis, or other backing services are required — `nxt-sts` is stateless.
38+
39+
## Option A — Deploy from GHCR
40+
41+
Use this when you want App Platform to run a pre-built release image without rebuilding from source.
42+
43+
### 1. Create the app
44+
45+
1. In the DigitalOcean control panel, go to **Apps → Create App**.
46+
2. Choose **Container Registry** (or **Deploy from a container image** depending on UI version).
47+
3. Select **GitHub Container Registry (GHCR)** as the registry type.
48+
4. Set the image to `ghcr.io/nxtgrid/nxt-sts` with tag `latest` or a specific version (e.g. `v1.0.0`).
49+
50+
Pinning a version tag is recommended for production; `latest` tracks the most recent release.
51+
52+
### 2. Configure registry access (private packages only)
53+
54+
If the GHCR package is not public:
55+
56+
1. Create a GitHub PAT with `read:packages` scope.
57+
2. In App Platform, add a registry credential for GHCR (username: your GitHub username, password: the PAT).
58+
3. Attach the credential to the app component.
59+
60+
### 3. Set HTTP port
61+
62+
1. Set **HTTP port** to `8080` (matches `EXPOSE 8080` in the Dockerfile).
63+
2. Leave the component as a **Web Service** (not a worker or job).
64+
65+
Health checks are configured after app creation in **Settings** — the default TCP check on port `8080` is usually sufficient for first deploy (see [Configure health checks](#configure-health-checks-settings-tab)).
66+
67+
### 4. Deploy
68+
69+
Review the plan and create the app. App Platform pulls the image and starts the container.
70+
71+
On first deploy, App Platform uses the default TCP readiness check on port `8080`. No change is required if the deploy succeeds.
72+
73+
To roll forward, push a new version tag in the repository (which triggers the GHCR release workflow), then update the App Platform component tag and redeploy.
74+
75+
## Option B — Build from GitHub
76+
77+
Use this when you want App Platform to build from the repository `Dockerfile` — for example during development, on a feature branch, or when you prefer not to depend on GHCR.
78+
79+
### 1. Create the app
80+
81+
1. In the DigitalOcean control panel, go to **Apps → Create App**.
82+
2. Choose **GitHub** as the source and authorize repository access if prompted.
83+
3. Select repository **`nxtgrid/nxt-sts`**.
84+
4. Select branch **`main`**.
85+
5. App Platform detects the root **`Dockerfile`** and configures a Docker build automatically.
86+
87+
### 2. Set HTTP port
88+
89+
1. Set **HTTP port** to `8080`.
90+
2. Confirm App Platform detected the root **`Dockerfile`** for the Docker build.
91+
92+
Configure the HTTP readiness probe after the first deploy only if you want stricter checks — see [Configure health checks](#configure-health-checks-settings-tab).
93+
94+
### 3. Enable auto-deploy (optional)
95+
96+
Turn on **Autodeploy** if App Platform should rebuild and redeploy on every push to the selected branch.
97+
98+
### 4. Deploy
99+
100+
Create the app. App Platform runs the multi-stage Docker build (Maven compile inside the build stage, JRE-only runtime image) and starts the service.
101+
102+
## Configure health checks (Settings tab)
103+
104+
Health checks are not configurable during initial app creation. After the first deploy, they appear under **Settings → Components → your web service → Health Checks**.
105+
106+
### Default: TCP readiness check
107+
108+
App Platform enables a **readiness** check by default: **TCP** on port `8080`, with no HTTP path or extra timing configured. This is the check that runs during deploy — it passes when something is listening on the port.
109+
110+
For `nxt-sts`, that default is often enough. Spring Boot binds to port `8080` when the HTTP server starts, so a TCP probe on `8080` typically lets the first deploy succeed without any changes.
111+
112+
**You do not need to change the default** if deploys complete and `/actuator/health` returns `UP` afterward.
113+
114+
What you may see labelled "liveness" in the UI is sometimes still this default readiness probe (TCP, minimal settings) — not a separate restart-on-failure liveness probe. A true liveness check is only present if you explicitly added one via **Add liveness check**.
115+
116+
### Optional upgrade: HTTP readiness check
117+
118+
Switching the readiness check from **TCP** to **HTTP** is recommended for production, but not required:
119+
120+
- **TCP** — port is open (may pass before Spring context is fully ready).
121+
- **HTTP** on `/actuator/health` — confirms the actuator reports `{"status":"UP"}`.
122+
123+
To upgrade:
124+
125+
1. Open **Apps** → your `nxt-sts` app → **Settings** tab.
126+
2. Under **Components**, click the web service component.
127+
3. Scroll to **Health Checks** and click **Edit**.
128+
4. Set **Type** to **HTTP**, **Port** to `8080`, **HTTP Path** to `/actuator/health`.
129+
5. Set timing to match the repository `Dockerfile` `HEALTHCHECK`:
130+
131+
```dockerfile
132+
HEALTHCHECK --interval=30s --timeout=5s --start-period=15s \
133+
CMD wget -qO- http://localhost:8080/actuator/health || exit 1
134+
```
135+
136+
| App Platform field | Value | Dockerfile flag |
137+
|---|---|---|
138+
| Initial delay | `15` seconds | `--start-period=15s` |
139+
| Period | `30` seconds | `--interval=30s` |
140+
| Timeout | `5` seconds | `--timeout=5s` |
141+
| Success threshold | `1` (default) | — (App Platform only; one passing probe marks the deploy healthy) |
142+
| Failure threshold | `9` (default) | — (App Platform only; no Docker equivalent) |
143+
144+
**Success / failure thresholds:** keep DigitalOcean defaults **`1`** and **`9`**. With a `30` s period, nine consecutive failures means roughly 4.5 minutes of sustained probe failure before App Platform rolls back — a good match for JVM cold starts when combined with the `15` s initial delay. Lower the failure threshold only if you want faster rollback on a genuinely broken deploy.
145+
146+
6. Click **Save** (triggers a redeploy with the new probe settings).
147+
148+
### Liveness check (optional)
149+
150+
A **liveness** probe is separate from readiness. It restarts the container if probes keep failing while the app is already running. You only need this if you explicitly click **Add liveness check** — the default TCP entry is not that.
151+
152+
If you add one, use **HTTP** on port `8080` with path `/actuator/health`, and the same timing as the readiness check (`15` s initial delay, `30` s period, `5` s timeout).
153+
154+
If you prefer infrastructure-as-code over the UI, the equivalent app-spec block is in [App spec reference](#app-spec-reference-optional) below.
155+
156+
## Post-deploy verification
157+
158+
Replace `<app-url>` with the App Platform default hostname or your custom domain.
159+
160+
**Health check:**
161+
162+
```bash
163+
curl -s https://<app-url>/actuator/health
164+
```
165+
166+
Expected response:
167+
168+
```json
169+
{"status":"UP"}
170+
```
171+
172+
**Service index:**
173+
174+
```bash
175+
curl -s https://<app-url>/
176+
```
177+
178+
Returns JSON listing available endpoints including `/token`, `/swagger`, and `/actuator/health`.
179+
180+
**Token generation smoke test:**
181+
182+
```bash
183+
curl -s -X POST https://<app-url>/token \
184+
-H "Content-Type: application/json" \
185+
-d '{
186+
"type": "TOP_UP",
187+
"issueDate": "2024-03-15T10:30:00",
188+
"randomNumber": 3,
189+
"decoderKey": "0123456789ABCDEF",
190+
"kwh": 0.5
191+
}'
192+
```
193+
194+
Use a real decoder key in non-test environments. Always call `/token` over HTTPS in production.
195+
196+
## App spec reference (optional)
197+
198+
Both options can also be defined in an App Platform spec file. Adjust `branch`, `tag`, and registry credentials to match your setup.
199+
200+
**GHCR image:**
201+
202+
```yaml
203+
name: nxt-sts
204+
services:
205+
- name: api
206+
image:
207+
registry_type: GHCR
208+
registry: ghcr.io
209+
repository: nxtgrid/nxt-sts
210+
tag: v1.0.0
211+
http_port: 8080
212+
instance_count: 1
213+
instance_size_slug: basic-xxs
214+
health_check:
215+
http_path: /actuator/health
216+
port: 8080
217+
initial_delay_seconds: 15
218+
period_seconds: 30
219+
timeout_seconds: 5
220+
success_threshold: 1
221+
failure_threshold: 9
222+
```
223+
224+
**GitHub + Dockerfile:**
225+
226+
```yaml
227+
name: nxt-sts
228+
services:
229+
- name: api
230+
github:
231+
repo: nxtgrid/nxt-sts
232+
branch: main
233+
deploy_on_push: true
234+
dockerfile_path: Dockerfile
235+
http_port: 8080
236+
instance_count: 1
237+
instance_size_slug: basic-xxs
238+
health_check:
239+
http_path: /actuator/health
240+
port: 8080
241+
initial_delay_seconds: 15
242+
period_seconds: 30
243+
timeout_seconds: 5
244+
success_threshold: 1
245+
failure_threshold: 9
246+
```
247+
248+
## Failure modes
249+
250+
| Symptom | Likely cause | What to check |
251+
|---|---|---|
252+
| Deploy fails pulling image | GHCR auth or missing release tag | Registry credentials; confirm a `v*.*.*` tag exists and the release workflow completed |
253+
| Health check never passes | TCP-only check or wrong path/port | In **Settings → Health Checks**, switch type to **HTTP**, port `8080`, path `/actuator/health`; set initial delay to `15` s if Spring Boot is still starting |
254+
| Build fails from GitHub | Docker build error in App Platform | Build logs; confirm `Dockerfile` and `./mvnw` are present on `main` |
255+
| 502/503 after deploy | Container crash on startup | App Platform runtime logs; verify Java 17 JRE image built successfully |
256+
| 400 on `/token` | Validation rejection | Swagger UI at `/swagger` for schema; check `randomNumber` (0–15) and 16-char hex `decoderKey` |

0 commit comments

Comments
 (0)