Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
eada685
feat(storage): define outbound stage contracts
lykakis Sep 28, 2026
a90b7e4
feat(storage): validate standalone SMS backend profiles
lykakis Sep 28, 2026
6b1653f
feat(storage): add memory pending and router stores
lykakis Sep 28, 2026
29147d7
feat(storage): add memory routed-work ownership
lykakis Sep 28, 2026
5264ea1
feat(storage): coordinate outbound lifecycle and terminal cleanup
lykakis Sep 29, 2026
70fb3a7
feat(storage): connect held multipart and protocol admission
lykakis Sep 29, 2026
3051c34
feat(storage): connect lifecycle routing and provider completion
lykakis Oct 1, 2026
7a2612f
feat(storage): schedule parent-owned provider retries
lykakis Oct 1, 2026
2b1f679
test(storage): preserve startup logs under CI log levels
lykakis Oct 1, 2026
67d514d
feat(storage): drain outbound executions before graceful shutdown
lykakis Oct 5, 2026
dc33da7
feat(storage): activate standalone message outbound pipeline
lykakis Oct 6, 2026
e74ddab
test(storage): verify combined outbound and embedded lifecycles
lykakis Oct 6, 2026
c23607e
refactor(storage): clarify outbound lifecycle API and documentation
lykakis Oct 8, 2026
38f93b3
test(storage): exclude routing fixture from CDI discovery
lykakis Oct 8, 2026
ec81336
fix(storage): reserve worker TPS permits only for queued messages
lykakis Oct 8, 2026
59a213f
fix(storage): skip router backlog scans during terminal cleanup
lykakis Oct 8, 2026
e1b3e4d
fix(storage): isolate provider preparation in per-worker pools
lykakis Oct 9, 2026
ab9d3bd
fix(storage): continue productive outbound batches without delay
lykakis Oct 9, 2026
ae31149
test(storage): address code-quality review annotations
lykakis Oct 9, 2026
d9f669e
fix(storage): restore outbound progress under sustained load
lykakis Oct 20, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,21 @@ These instructions apply to all work in this repository.
- Before merging, review all commit messages that will land on `main`, including their bodies. Release Please can interpret additional conventional-commit messages in a body as separate changelog entries. Prefer a squash merge with one capability-level summary for a cohesive feature, and preserve the required DCO trailer.
- Before a release, review the generated changelog and consolidate redundant entries. Keep `CHANGELOG.md` and the release PR description consistent. Release Please may regenerate both, so verify the curated entries after its final run; do not assume a manual PR-description edit is persistent.

## Scope Ownership and Downstream Consumers

- Implement only behavior required by this repository's approved task. Do not expand scope based on features discovered in downstream consumers, related repositories, or hypothetical future integrations.
- A request to review compatibility or keep contracts reusable authorizes compatibility analysis and appropriate extension boundaries. It does not authorize implementing consumer-specific workflows, adapters, policies, or lifecycle behavior.
- Requirements belonging to another repository or team remain their responsibility. Record relevant findings and explain potential integration needs; do not implement them without explicit approval.
- Before adding behavior primarily motivated by another consumer, identify:
1. Which current-task requirement needs it.
2. Which repository or team owns that behavior.
3. Whether the task can be completed without it.
If it is not required for the approved task, defer it.
- Present any proposed scope expansion separately, with its motivation, implementation impact, and a recommendation. Wait for explicit approval before adding it to the implementation plan or writing code. General approval to proceed does not authorize an undisclosed expansion.
- Do not introduce speculative abstractions or implementation machinery merely to make future adoption easier. Prefer the smallest contract that satisfies the current task and leaves a clear extension path.
- When a missing prerequisite is discovered, distinguish work genuinely necessary to complete the approved task from optional support for another consumer. Do not describe optional consumer support as a required prerequisite.
- Apply this scope check when creating the plan, not only while coding. Clearly label cross-repository additions so they cannot enter scope through a broad task title or an assumed design decision.

## Commits

- Every commit must include a Developer Certificate of Origin `Signed-off-by:` trailer. Create commits with `git commit -s` and use the contributor's configured name and email.
Expand Down
13 changes: 12 additions & 1 deletion docs/04-smpp-configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,20 @@ All properties below should be prefixed with your instance path. For example, if
| `charset.latin1` | `ISO-8859-1` | The default charset mapping for Latin-1. |
| `charset.ucs2` | `UCS-2` | The default charset mapping for UCS-2 (data coding `8`). |
| `ccat.8bit` | `true` | Use 8-bit reference numbers for Concatenated (multipart) SMS instead of 16-bit. |
| `reassembling.timeoutMillis` | `30000` | Timeout (in ms) to wait for all parts of a concatenated message to arrive before failing. |
| `reassembling.timeoutMillis` | `30000` | Time (in ms) to wait for concatenated-message parts; incomplete groups release their received parts individually on expiry. |
| `filters.beforeInsertMessage` | `""` | (Filters Not supported yet) Comma-separated list of filter class names to process messages before queuing. |

### Admission and acknowledgement

The standalone server uses the shared memory outbound coordinator. It accepts a complete message, or retains
each concatenated part as held work, before success. Parts are acknowledged individually rather than
waiting for the whole message. Completed/expired reassembly work retains those original source IDs.

In that coordinated path, capacity rejection returns `STATUS_THROTTLED`, unsupported input returns
`STATUS_SUBMITFAIL`, and other lifecycle/storage failures return `STATUS_SYSERR`. A failure after
acknowledgement during ready publication is retried without another client response. Embedded workers
without a coordinator binding retain local ingress-queue admission before success. See [Outbound Message Storage](14-outbound-storage.md).

## 🧵 Thread Pool Configuration

| Property | Default Value | Description |
Expand Down
10 changes: 6 additions & 4 deletions docs/06-http-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,11 @@ The API returns standard HTTP status codes along with a plain-text response body

| HTTP Status | Meaning | Description |
| :--- | :--- | :--- |
| **`202 Accepted`** | **Success** | The message was validated and inserted into Sendium's router queue. The response body contains the unique UUID (serial) of the message. |
| **`400 Bad Request`** | **Error** | Missing a required parameter (`to`, `from`, or `text`). The response body details which parameter is missing. |
| **`202 Accepted`** | **Success** | Admission succeeded through the configured path. The response body contains the unique gateway UUID (serial) of the message. |
| **`400 Bad Request`** | **Error** | Missing a required parameter (`to`, `from`, or `text`), or the selected lifecycle cannot support the submission. |
| **`401 Unauthorized`** | **Error** | Invalid or missing credentials. |
| **`500 Server Error`** | **Error** | An internal error occurred while parsing or processing the message payload. |
| **`503 Unavailable`** | **Error** | Internal queue admission was interrupted. The message was not queued; the client should retry later. |
| **`503 Unavailable`** | **Error** | Admission is unavailable, at capacity, has an ownership conflict, or was interrupted. The client should retry later. |

---

Expand Down Expand Up @@ -90,7 +90,9 @@ For a manual installation, replace the environment variables with the HTTP `syst
123e4567-e89b-12d3-a456-426614174000
```

`202 Accepted` means Sendium validated the message and inserted it into the in-memory router queue. It does not write an outbound message or DLR row to PostgreSQL, and it does not prove that a viable route exists, that an upstream SMSC accepted the message, or that a handset received it. Durable DLR state is created only after an upstream provider outcome. Local-only Quick Start installations have no outbound route. Check routing configuration, the SMPP client connection, message lifecycle logs, submit response, and delivery receipt for those later stages.
In the standalone runtime, `202 Accepted` means admission into the memory-backed pending store. The source remains retained through routing, provider processing, and required DLR handoffs. Capacity rejection returns `503`; a failing coordinator is never replaced by legacy queue admission. Embedded callers without a coordinator retain the legacy in-memory queue path. The memory profile is non-durable across restart. See [Outbound Message Storage](14-outbound-storage.md).

Neither memory admission path writes an outbound message or DLR row to PostgreSQL. Acceptance does not prove that a viable route exists, that an upstream SMSC accepted the message, or that a handset received it. Durable DLR state is created only after an upstream provider outcome. Local-only Quick Start installations have no outbound route. Check routing configuration, the SMPP client connection, message lifecycle logs, submit response, and delivery receipt for those later stages.

## Related Documentation

Expand Down
39 changes: 39 additions & 0 deletions docs/09-configuration-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,44 @@ In the Docker image, the working directory is `/work`, so the default configurat
| `QUARKUS_HTTP_ACCESS_LOG_ENABLE` | `true` | Enables HTTP access logging. |
| `QUARKUS_HTTP_ACCESS_LOG_DIRECTORY` | `/work/logs` | HTTP access log directory. |

## Outbound Message Storage Profile

The standalone application reads these global, startup-only settings from Quarkus configuration
(`application.properties`, JVM `-D` properties, or the environment). They are not hot-reloaded worker
settings in `smsg.properties`.

| Property | Environment variable | Default |
| :--- | :--- | :--- |
| `sendium.message.pending.backend` | `SENDIUM_MESSAGE_PENDING_BACKEND` | `memory` |
| `sendium.message.router-queue.backend` | `SENDIUM_MESSAGE_ROUTER_QUEUE_BACKEND` | `memory` |
| `sendium.message.routed-work.backend` | `SENDIUM_MESSAGE_ROUTED_WORK_BACKEND` | `memory` |
| `sendium.message.pending.capacity` | `SENDIUM_MESSAGE_PENDING_CAPACITY` | `10000` |
| `sendium.message.router-queue.capacity` | `SENDIUM_MESSAGE_ROUTER_QUEUE_CAPACITY` | `1000` |
| `sendium.message.selection-batch-size` | `SENDIUM_MESSAGE_SELECTION_BATCH_SIZE` | `100` |

Only **`memory/memory/memory`** is currently selectable. Missing settings default to `memory`;
explicitly empty, unknown, or unsupported values fail startup. Values are case-sensitive. Requests
such as `file/file/memory`, partial-file combinations, or any PostgreSQL outbound combination fail
with an error naming the requested profile, supported profile, and configuration keys. There is no
fallback to a less durable backend.

Validation runs before Sendium's file-watcher and router/worker startup observers. The validated
profile is retained for the process lifetime. Startup logs the effective profile and a prominent
`NON-DURABLE` warning: accepted messages and in-flight work can be lost on restart. The current
standalone pipeline binds HTTP/SMPP to one shared coordinator and executes bounded selection, routing,
SMPP-client submission, retries and terminal handoffs. All capacity/batch settings must be positive.
Pending capacity counts retained accepted sources (including held multipart parts); router capacity
counts queued plus taken unassigned selections. Routed capacity equals pending source capacity, so
normal source-retaining operation does not introduce a smaller destination-record bound. Partial terminal
cleanup may still occupy routed slots after sources are removed; such cleanup failures require retry.
Selection and each destination take loop process at most the configured batch size per poll.
See [Outbound Message Storage](14-outbound-storage.md).

The producer and startup observer live only in `sendium-app`. Embedding `sendium-core` does not
activate this validation, configure an outbound coordinator, or enforce the standalone profile list.
Embedding applications own their assembly. Outbound message selectors do not change the independently
configured DLR subsystem below.

## DLR Storage Environment Variables

| Variable | Default | Description |
Expand Down Expand Up @@ -98,3 +136,4 @@ When the HTTP server is running, Sendium exposes:
- [SMPP Configuration](04-smpp-configuration.md)
- [Routing Engine](05-routing-engine.md)
- [DLR Persistence](13-dlr-persistence.md)
- [Outbound Message Storage](14-outbound-storage.md)
Loading
Loading