diff --git a/CHANGELOG.md b/CHANGELOG.md
index b305c568..b105af9f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,9 +2,36 @@
All notable changes to taskito are documented here. The format is based on
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/); the project follows
-[Semantic Versioning](https://semver.org/spec/v2.0.0.html). All SDKs (Python, Node) and the
+[Semantic Versioning](https://semver.org/spec/v2.0.0.html). All SDKs (Python, Node, Java) and the
underlying Rust crates are released together, in lock-step.
+## 0.17.0
+
+Feature release across the SDKs: periodic-task management, task-scoped dead-letter queries, and
+the first **Java SDK** release.
+
+### Added
+
+- **Java SDK.** First release, published to Maven Central as `org.byteveda:taskito`. A JNI binding
+ over the Taskito core with the full producer / worker / inspection / admin surface, workflows,
+ distributed locks, periodic tasks, an in-memory `taskito-test` backend, and a `@TaskHandler`
+ annotation processor. Verified under GraalVM `native-image`.
+- **Periodic task management.** List, unschedule, pause, and resume registered cron tasks —
+ `listPeriodic` / `deletePeriodic` / `pausePeriodic` / `resumePeriodic` (Java, Node) and the
+ equivalent native bindings (Python). Pause toggles the task's `enabled` flag without removing it
+ (#313).
+- **Per-task dead-letter queries.** List and purge dead-letter entries scoped to a single task —
+ `listDeadByTask` / `purgeDeadByTask` (Java), `deadLettersByTask` / `purgeDeadByTask` (Node), and
+ native bindings (Python). Filtering happens server-side so pagination stays correct (#314).
+- **Per-task retry backoff (Java).** `Task.retryPolicy(...)` surfaces the core retry engine's
+ backoff curve — exponential with jitter, or exact explicit delays — with the retry budget still
+ set per enqueue (#311).
+
+### Changed
+
+- **Node now requires Node.js 20+.** Node 18 reached end-of-life; the test toolchain (vitest 4)
+ requires Node 20.12+. The `engines` floor and tsup target were raised accordingly (#312).
+
## 0.16.4
Maintenance release. Upgrades the Rust↔Python binding layer and refreshes project docs; no
diff --git a/Cargo.lock b/Cargo.lock
index 38dee34e..b52ff5c5 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1481,7 +1481,7 @@ checksum = "adb6935a6f5c20170eeceb1a3835a49e12e19d792f6dd344ccc76a985ca5a6ca"
[[package]]
name = "taskito-core"
-version = "0.16.4"
+version = "0.17.0"
dependencies = [
"async-trait",
"chrono",
@@ -1505,7 +1505,7 @@ dependencies = [
[[package]]
name = "taskito-java"
-version = "0.16.4"
+version = "0.17.0"
dependencies = [
"async-trait",
"crossbeam-channel",
@@ -1523,7 +1523,7 @@ dependencies = [
[[package]]
name = "taskito-mesh"
-version = "0.16.4"
+version = "0.17.0"
dependencies = [
"base64",
"bincode",
@@ -1538,7 +1538,7 @@ dependencies = [
[[package]]
name = "taskito-node"
-version = "0.16.4"
+version = "0.17.0"
dependencies = [
"async-trait",
"crossbeam-channel",
@@ -1557,7 +1557,7 @@ dependencies = [
[[package]]
name = "taskito-python"
-version = "0.16.4"
+version = "0.17.0"
dependencies = [
"async-trait",
"base64",
@@ -1577,7 +1577,7 @@ dependencies = [
[[package]]
name = "taskito-workflows"
-version = "0.16.4"
+version = "0.17.0"
dependencies = [
"dagron-core",
"diesel",
diff --git a/crates/taskito-core/Cargo.toml b/crates/taskito-core/Cargo.toml
index 293d78f1..c6e1464c 100644
--- a/crates/taskito-core/Cargo.toml
+++ b/crates/taskito-core/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "taskito-core"
-version = "0.16.4"
+version = "0.17.0"
edition = "2021"
[features]
diff --git a/crates/taskito-java/Cargo.toml b/crates/taskito-java/Cargo.toml
index 4c1c9883..93d47a07 100644
--- a/crates/taskito-java/Cargo.toml
+++ b/crates/taskito-java/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "taskito-java"
-version = "0.16.4"
+version = "0.17.0"
edition = "2021"
description = "Java (JNI) bindings for the Taskito task-queue core"
license = "MIT"
diff --git a/crates/taskito-mesh/Cargo.toml b/crates/taskito-mesh/Cargo.toml
index 5ed7db3c..0cd76aff 100644
--- a/crates/taskito-mesh/Cargo.toml
+++ b/crates/taskito-mesh/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "taskito-mesh"
-version = "0.16.4"
+version = "0.17.0"
edition = "2021"
[dependencies]
diff --git a/crates/taskito-node/Cargo.toml b/crates/taskito-node/Cargo.toml
index bba056d6..c3f85267 100644
--- a/crates/taskito-node/Cargo.toml
+++ b/crates/taskito-node/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "taskito-node"
-version = "0.16.4"
+version = "0.17.0"
edition = "2021"
description = "Node.js (napi-rs) bindings for the Taskito task-queue core"
license = "MIT"
diff --git a/crates/taskito-python/Cargo.toml b/crates/taskito-python/Cargo.toml
index 27f133e8..055a74f9 100644
--- a/crates/taskito-python/Cargo.toml
+++ b/crates/taskito-python/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "taskito-python"
-version = "0.16.4"
+version = "0.17.0"
edition = "2021"
[features]
diff --git a/crates/taskito-workflows/Cargo.toml b/crates/taskito-workflows/Cargo.toml
index a2e4c181..3ab8041c 100644
--- a/crates/taskito-workflows/Cargo.toml
+++ b/crates/taskito-workflows/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "taskito-workflows"
-version = "0.16.4"
+version = "0.17.0"
edition = "2021"
[features]
diff --git a/docs/app/components/landing/hero.tsx b/docs/app/components/landing/hero.tsx
index 2ee9026f..216e559f 100644
--- a/docs/app/components/landing/hero.tsx
+++ b/docs/app/components/landing/hero.tsx
@@ -92,9 +92,12 @@ export function Hero() {
onClick={() => setSdk(p.id === "ts" ? "node" : "python")}
>
{p.label}
- {p.id === "ts" ? Beta : null}
))}
+