Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
99 changes: 99 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
name: Release

on:
workflow_dispatch:
inputs:
version:
description: "Release version tag (for example: v0.3.0)"
required: true
type: string
target:
description: "Target branch or commit-ish for the release tag"
required: false
default: "main"
type: string

permissions:
contents: write

jobs:
release:
name: Tag and Publish Release
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Validate release input
id: validate
env:
INPUT_VERSION: ${{ inputs.version }}
INPUT_TARGET: ${{ inputs.target }}
run: |
set -euo pipefail
version="${INPUT_VERSION}"
target="${INPUT_TARGET}"
if [ -z "$target" ]; then
target="main"
fi

if [[ ! "$version" =~ ^v[0-9]+\.[0-9]+\.[0-9]+([-.][0-9A-Za-z.-]+)?$ ]]; then
echo "invalid version format: $version"
echo "expected: v<major>.<minor>.<patch> (optional pre-release/build suffix)"
exit 1
fi

git fetch --force --tags origin
git fetch --prune origin
if git rev-parse -q --verify "refs/tags/${version}" >/dev/null; then
echo "tag already exists: $version"
exit 1
fi

target_ref="$target"
if git rev-parse -q --verify "origin/$target^{commit}" >/dev/null; then
target_ref="origin/$target"
elif ! git rev-parse -q --verify "$target^{commit}" >/dev/null; then
echo "target commit-ish not found: $target"
exit 1
fi

echo "version=$version" >> "$GITHUB_OUTPUT"
echo "target_ref=$target_ref" >> "$GITHUB_OUTPUT"

- name: Extract release notes from CHANGELOG
id: notes
env:
VERSION: ${{ steps.validate.outputs.version }}
run: |
set -euo pipefail
notes_file="$RUNNER_TEMP/release-notes.md"
./scripts/release-notes.sh "$VERSION" > "$notes_file"
echo "notes_file=$notes_file" >> "$GITHUB_OUTPUT"

- name: Create and push release tag
env:
VERSION: ${{ steps.validate.outputs.version }}
TARGET_REF: ${{ steps.validate.outputs.target_ref }}
run: |
set -euo pipefail
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git tag -a "$VERSION" -m "release $VERSION" "$TARGET_REF"
git push origin "refs/tags/$VERSION"

- name: Publish GitHub release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
VERSION: ${{ steps.validate.outputs.version }}
TARGET: ${{ inputs.target }}
NOTES_FILE: ${{ steps.notes.outputs.notes_file }}
run: |
set -euo pipefail
args=(release create "$VERSION" --title "$VERSION" --target "$TARGET" --notes-file "$NOTES_FILE")
if [[ "$VERSION" == *-* ]]; then
args+=(--prerelease)
fi
gh "${args[@]}"
35 changes: 33 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,35 @@

All notable changes to this project are documented in this file.

## [Unreleased]

### Added

- release workflow (`.github/workflows/release.yml`) for tag+GitHub Release from merged PR state
- changelog note extractor script: `scripts/release-notes.sh`
- production deployment and data safety guide: `docs/production.md`

### Changed

- `scripts/integration-local.sh` now supports:
- `--no-start`
- `--port <port>`
- `--timeout <seconds>`
- `--verbose`
- explicit `--` passthrough for extra `go test` args
- docs expanded for harness options and release process (`README.md`, `CONTRIBUTING.md`, `integration/README.md`)

## [v0.2.1] - 2026-02-27

### Added

- local integration harness script:
- starts `simplex-chat`
- waits for websocket readiness
- runs integration tests
- cleans up process on exit
- documentation updates for local harness usage in README and integration docs

## [v0.2.0] - 2026-02-27

### Added
Expand Down Expand Up @@ -43,5 +72,7 @@ All notable changes to this project are documented in this file.
- rate limiting and safety validations
- scaffold command with `basic` and `moderation` templates

[ v0.2.0 ]: https://github.com/Malomalsky/go-simplex/compare/v0.1.0...v0.2.0
[ v0.1.0 ]: https://github.com/Malomalsky/go-simplex/releases/tag/v0.1.0
[v0.2.1]: https://github.com/Malomalsky/go-simplex/compare/v0.2.0...v0.2.1
[v0.2.0]: https://github.com/Malomalsky/go-simplex/compare/v0.1.0...v0.2.0
[v0.1.0]: https://github.com/Malomalsky/go-simplex/releases/tag/v0.1.0
[Unreleased]: https://github.com/Malomalsky/go-simplex/compare/v0.2.1...HEAD
16 changes: 16 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,24 @@ For local reproducible runs, use:
./scripts/integration-local.sh
```

Example with custom options:

```bash
./scripts/integration-local.sh --no-start --port 5225 --timeout 90 -- -run TestLiveContracts
```

Additional fixture variables are documented in `integration/README.md`.

## Release process

Releases are done from merged PR state:

1. Add/update changelog section in `CHANGELOG.md` (`## [vX.Y.Z] - YYYY-MM-DD`).
2. Merge PR to `main`.
3. Run GitHub Actions workflow `Release` with input `version=vX.Y.Z`.

The workflow validates the tag, extracts notes from `CHANGELOG.md`, creates/pushes the tag, and publishes GitHub Release.

## Pull requests

- keep PRs focused (one concern per PR)
Expand Down
28 changes: 28 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ More details: `docs/security.md`.
- Bot development guide: `docs/bot-development.md`
- Compatibility and coverage: `docs/compatibility.md`
- Security guide: `docs/security.md`
- Production deployment guide: `docs/production.md`
- Vulnerability reporting: `SECURITY.md`
- Contribution guide: `CONTRIBUTING.md`
- Upstream API research notes: `docs/research/upstream-api.md`
Expand Down Expand Up @@ -179,8 +180,35 @@ Local harness (auto-start `simplex-chat`, run integration tests, cleanup):
./scripts/integration-local.sh
```

Harness options:

- `--no-start` to run against existing endpoint
- `--port <port>` to override local websocket port
- `--timeout <sec>` to adjust readiness wait
- `--verbose` for extra diagnostics

Pass extra `go test` args after `--`, for example:

```bash
./scripts/integration-local.sh --no-start -- -run TestLiveContracts
```

Optional vulnerability scan:

```bash
go run golang.org/x/vuln/cmd/govulncheck@latest ./...
```

## Release process

Release from merged PR state:

1. add a version section to `CHANGELOG.md` (`## [vX.Y.Z] - YYYY-MM-DD`)
2. merge to `main`
3. run GitHub Actions workflow `Release` with input `version=vX.Y.Z`

Release notes are extracted from changelog via:

```bash
./scripts/release-notes.sh vX.Y.Z
```
1 change: 1 addition & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
- Bot development guide: `bot-development.md`
- Compatibility and coverage: `compatibility.md`
- Security and data safety: `security.md`
- Production deployment guide: `production.md`
- Scaffold templates: `basic`, `moderation` via `cmd/simplexbot-init`
- Runnable examples index: `../examples/README.md`
- Live contract tests: `../integration/README.md`
Expand Down
136 changes: 136 additions & 0 deletions docs/production.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
# Production Deployment Guide

This guide describes a practical baseline for running `go-simplex` bots in production with strong data-safety defaults.

Official references:

- SimpleX bot overview: https://github.com/simplex-chat/simplex-chat/tree/stable/bots
- Bot API commands: https://github.com/simplex-chat/simplex-chat/blob/stable/bots/api/COMMANDS.md
- Bot API events: https://github.com/simplex-chat/simplex-chat/blob/stable/bots/api/EVENTS.md

## 1) Deployment model

Recommended topology:

- `simplex-chat` process exposing websocket API
- one or more Go bot processes using `go-simplex` client/runtime
- process supervisor (`systemd`) for restart and controlled rollout

Keep bot runtime and SimpleX state on the same trusted host when possible.

## 2) Service account and file permissions

- create dedicated non-login user for services (for example `simplexbot`)
- ensure SimpleX state directory and bot config directories are owned by this user
- set strict permissions:
- directories: `0700`
- secrets/env files: `0600`
- avoid running either process as `root`

## 3) Network and TLS

- default to loopback binding for websocket (`ws://127.0.0.1:<port>`) when bot runs on same host
- if remote access is required, terminate TLS at reverse proxy and expose only `wss://`
- restrict ingress by source IP/network; do not expose raw local websocket publicly

In bot code, enforce transport hardening:

- `ws.WithRequireWSS(true)` for remote links
- `ws.WithTLSMinVersion(...)`
- `ws.WithReadLimit(...)`

## 4) Secrets and sensitive data

- pass credentials via environment or secret manager, not command-line flags
- do not commit `.env`, private keys, websocket credentials, or fixture IDs
- avoid logging message payloads and raw command bodies in production
- rotate credentials and tokens on schedule and after incidents

## 5) Runtime hardening in SDK

Use these controls by default for production bots:

- strict outbound raw-command policy:
- `client.WithRawCommandAllowPrefixes(...)`
- `client.WithRawCommandValidator(...)`
- `client.WithRawCommandMaxBytes(...)`
- bounded channels and backpressure:
- `client.WithEventOverflowPolicy(...)`
- `client.WithErrorOverflowPolicy(...)`
- `client.WithDropHandler(...)`
- forward compatibility:
- `client.WithStrictResponses(false)` during upstream migrations
- abuse mitigation:
- `router.EnablePerContactRateLimit(...)`

## 6) Observability and incident response

- collect process logs via `journald`/central log pipeline
- add restart alerts (unexpected exits, restart loops)
- track bot-level metrics:
- reconnect count
- command latency
- dropped events/errors
- keep `SECURITY.md` process visible for coordinated disclosure

## 7) Backup and restore

- backup SimpleX state directory and bot configuration daily (encrypted at rest)
- verify restore in a non-production environment on a schedule
- document RPO/RTO and operational owner
- during restore drills, validate:
- websocket bootstrap
- command send path
- event handling path

## 8) Example systemd units

`simplex-chat.service`:

```ini
[Unit]
Description=SimpleX Chat API
After=network.target

[Service]
User=simplexbot
Group=simplexbot
WorkingDirectory=/opt/go-simplex
ExecStart=/usr/local/bin/simplex-chat -p 5225
Restart=always
RestartSec=2
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true

[Install]
WantedBy=multi-user.target
```

`go-simplex-bot.service`:

```ini
[Unit]
Description=Go SimpleX Bot
After=network.target simplex-chat.service
Requires=simplex-chat.service

[Service]
User=simplexbot
Group=simplexbot
WorkingDirectory=/opt/go-simplex-bot
Environment="SIMPLEX_WS_URL=ws://127.0.0.1:5225"
ExecStart=/opt/go-simplex-bot/my-bot
Restart=always
RestartSec=2
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true

[Install]
WantedBy=multi-user.target
```

Adjust hardening options if your runtime needs additional filesystem access.
13 changes: 13 additions & 0 deletions integration/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,19 @@ Use existing websocket instead of starting local process:
SIMPLEX_WS_URL=ws://localhost:5225 ./scripts/integration-local.sh
```

Or with explicit flags:

```bash
./scripts/integration-local.sh --no-start --port 5225 --timeout 90 -- -run TestLiveContracts
```

Useful harness options:

- `--no-start` - do not launch `simplex-chat`; run against existing endpoint
- `--port <port>` - local websocket port for auto-start and default endpoint
- `--timeout <seconds>` - readiness wait budget when auto-starting
- `--verbose` - extra diagnostics for readiness/wait steps

## Optional fixture env vars for extended flows

Set these to enable additional contract tests:
Expand Down
Loading