Skip to content
Open
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
b9fe77c
add Settings domain with DB persistence and admin CRUD API
bitcoin-coder-bob Feb 27, 2026
ae7da6d
remove settings env vars, seed defaults from DB with scheduler-aware …
bitcoin-coder-bob Feb 27, 2026
ceb6d68
lint, make proto
bitcoin-coder-bob Feb 27, 2026
2fbbeb3
Merge remote-tracking branch 'origin/master' into bob/settings-domain…
bitcoin-coder-bob Feb 27, 2026
d0564c0
Merge branch 'master' into bob/settings-domain-and-repo
bitcoin-coder-bob Mar 2, 2026
0c9a296
update im memory config values after persisting to db
bitcoin-coder-bob Mar 2, 2026
da53ecf
merge branch 'master' into bob/settings-domain-and-repo
bitcoin-coder-bob Mar 18, 2026
5271785
validate settings, update servce setting on ClearSettings, dedpup loc…
bitcoin-coder-bob Mar 18, 2026
a61a9f1
README update for settings endpoints, swtting mutex usage, default ma…
bitcoin-coder-bob Mar 18, 2026
2802d62
error returning
bitcoin-coder-bob Mar 18, 2026
557ccad
wrning logs on setting changes, comments
bitcoin-coder-bob Mar 18, 2026
d382b18
Add uint32 overflow guards, amount min/max validation, fix ClearSetti…
bitcoin-coder-bob Mar 18, 2026
4b5966b
add BanThreshold validation, remove redundant Clear in ClearSettings,…
bitcoin-coder-bob Mar 18, 2026
066e9f7
Merge branch 'master' into bob/settings-domain-and-repo
bitcoin-coder-bob Mar 19, 2026
27ab8a1
remove unused default config vars
bitcoin-coder-bob Mar 19, 2026
70fb5fe
Merge branch 'master' into bob/settings-domain-and-repo
bitcoin-coder-bob Mar 25, 2026
dd48374
Validate CheckpointExitDelay in settings
bitcoin-coder-bob Mar 25, 2026
b21375a
Support partial updates in UpdateSettings
bitcoin-coder-bob Mar 25, 2026
93010e2
lint
bitcoin-coder-bob Mar 25, 2026
232d5a1
add update_fields to UpdateSettingsRequest for partial updates
bitcoin-coder-bob Mar 25, 2026
4fd6fbb
Validate update_fields and derive valid names from struct
bitcoin-coder-bob Mar 25, 2026
3c78e95
Merge branch 'master' into bob/settings-domain-and-repo
bitcoin-coder-bob Apr 2, 2026
c094527
lint
bitcoin-coder-bob Apr 2, 2026
fd134d5
Merge master into bob/settings-domain-and-repo
bitcoin-coder-bob Apr 14, 2026
1fc333e
remove unused config var defaultAllowCSVBlockType
bitcoin-coder-bob Apr 15, 2026
2f660ca
seed DB-backed settings from env vars on first boot
bitcoin-coder-bob Apr 15, 2026
29e4f2b
validate seeded default settings before persisting on first boot
bitcoin-coder-bob Apr 15, 2026
0f734ec
Merge master into bob/settings-domain-and-repo
bitcoin-coder-bob Apr 24, 2026
5142125
Remove unused default constants introduced during merge
bitcoin-coder-bob Apr 24, 2026
ece9dd1
Merge master into bob/settings-domain-and-repo
bitcoin-coder-bob Apr 27, 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
21 changes: 5 additions & 16 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -181,26 +181,15 @@ run-signer:
@go run ./cmd/arkd-wallet

## run-simulation: run the multi-VTXO batch settlement test
## Usage: make run-simulation [CLIENTS=n] [MIN=n] [MAX=n]
## Usage: make run-simulation [CLIENTS=n]
# Examples:
# make run-simulation # Default: 5 clients, min=5, max=128
# make run-simulation CLIENTS=10 # 10 clients, min=10, max=128
# make run-simulation CLIENTS=10 MAX=10 # 10 clients, exact batch size of 10
# make run-simulation CLIENTS=20 MIN=5 # 20 clients, minimum batch size of 5
# make run-simulation # Default: 5 clients
# make run-simulation CLIENTS=10 # 10 clients
run-simulation:
@echo "Stopping any existing Docker environment..."
@docker compose -f docker-compose.regtest.yml down -v 2>/dev/null || true
@echo "Starting Docker environment with batch configuration..."
@bash -c '\
CLIENTS="$${CLIENTS:-5}"; \
MIN="$${MIN:-$$CLIENTS}"; \
MAX="$${MAX:-128}"; \
echo "Configuration: CLIENTS=$$CLIENTS, MIN=$$MIN, MAX=$$MAX"; \
ARKD_ROUND_MIN_PARTICIPANTS_COUNT=$$MIN \
ARKD_ROUND_MAX_PARTICIPANTS_COUNT=$$MAX \
ARKD_SESSION_DURATION=60 \
docker compose -f docker-compose.regtest.yml up --build -d; \
'
@echo "Starting Docker environment..."
@ARKD_SESSION_DURATION=60 docker compose -f docker-compose.regtest.yml up --build -d
@echo "Waiting for services to start..."
@sleep 30
@bash -c '\
Expand Down
45 changes: 32 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,9 @@ In this documentation, you'll learn how to install and use `arkd`, a Bitcoin ser

### Configuration Options

The `arkd` server can be configured using environment variables.
The `arkd` server can be configured using environment variables and the admin settings API.

#### Environment Variables

| Environment Variable | Description | Default |
|-------------------------------------|---------------------------------------------------------------------------------|--------------------------------|
Expand All @@ -73,9 +75,6 @@ The `arkd` server can be configured using environment variables.
| `ARKD_LIVE_STORE_TYPE` | Cache service type (redis, inmemory) | `redis` |
| `ARKD_REDIS_URL` | Redis db connection url if `ARKD_LIVE_STORE_TYPE` is set to `redis` | - |
| `ARKD_REDIS_NUM_OF_RETRIES` | Maximum number of retries for Redis write operations in case of conflicts | - |
| `ARKD_VTXO_TREE_EXPIRY` | VTXO tree expiry in seconds | `604672` (7 days) |
| `ARKD_UNILATERAL_EXIT_DELAY` | Unilateral exit delay in seconds | `86400` (24 hours) |
| `ARKD_BOARDING_EXIT_DELAY` | Boarding exit delay in seconds | `7776000` (3 months) |
| `ARKD_ESPLORA_URL` | Esplora API URL | `https://blockstream.info/api` |
| `ARKD_WALLET_ADDR` | The arkd wallet address to connect to in the form `host:port` | - |
| `ARKD_SIGNER_ADDR` | The signer address to connect to in the form `host:port` | value of `ARKD_WALLET_ADDR` |
Expand All @@ -84,16 +83,7 @@ The `arkd` server can be configured using environment variables.
| `ARKD_UNLOCKER_TYPE` | Wallet unlocker type (env, file) to enable auto-unlock | - |
| `ARKD_UNLOCKER_FILE_PATH` | Path to unlocker file | - |
| `ARKD_UNLOCKER_PASSWORD` | Wallet unlocker password | - |
| `ARKD_ROUND_MAX_PARTICIPANTS_COUNT` | Maximum number of participants per round | `128` |
| `ARKD_ROUND_MIN_PARTICIPANTS_COUNT` | Minimum number of participants per round | `1` |
| `ARKD_UTXO_MAX_AMOUNT` | The maximum allowed amount for boarding or collaborative exit | `-1` (unset) |
| `ARKD_UTXO_MIN_AMOUNT` | The minimum allowed amount for boarding or collaborative exit | `-1` (dust) |
| `ARKD_VTXO_MAX_AMOUNT` | The maximum allowed amount for vtxos | `-1` (unset) |
| `ARKD_VTXO_MIN_AMOUNT` | The minimum allowed amount for vtxos | `-1` (dust) |
| `ARKD_BAN_DURATION` | Ban duration in seconds | `300` (5 minutes) |
| `ARKD_BAN_THRESHOLD` | Number of crimes to trigger a ban | `3` |
| `ARKD_SCHEDULER_TYPE` | Scheduler type (gocron, block) | `gocron` |
| `ARKD_CHECKPOINT_EXIT_DELAY` | Checkpoint exit delay in seconds | `86400` (24 hours) |
| `ARKD_TLS_EXTRA_IP` | Extra IP addresses for TLS (comma-separated) | - |
| `ARKD_TLS_EXTRA_DOMAIN` | Extra domains for TLS (comma-separated) | - |
| `ARKD_NOTE_URI_PREFIX` | Note URI prefix | - |
Expand All @@ -109,6 +99,35 @@ The `arkd` server can be configured using environment variables.
| `ARKD_HEARTBEAT_INTERVAL` | Heartbeat interval in seconds | `60` |
| `ARKD_ROUND_REPORT_ENABLED` | Enable round report service | `false` |

#### Admin Settings

The following settings are persisted in the database and managed via the admin API. Default values are seeded on first startup.

| Endpoint | Method | Description |
|-----------------------------------|--------|------------------------------------------|
| `/v1/admin/settings` | GET | Retrieve current settings |
| `/v1/admin/settings` | POST | Update settings (full replace) |
| `/v1/admin/settings/clear` | POST | Reset settings to defaults |

| Setting | Description | Default |
|--------------------------------------|--------------------------------------------------------------|--------------------------------|
| `vtxo_tree_expiry` | VTXO tree expiry (blocks or seconds depending on scheduler) | `604672` (gocron) / `20` (block) |
| `unilateral_exit_delay` | Unilateral exit delay in seconds | `86400` (24 hours) |
| `public_unilateral_exit_delay` | Public unilateral exit delay in seconds | `86400` (24 hours) |
| `checkpoint_exit_delay` | Checkpoint exit delay (blocks or seconds) | `86400` (gocron) / `10` (block) |
| `boarding_exit_delay` | Boarding exit delay in seconds | `7776000` (3 months) |
| `round_min_participants_count` | Minimum number of participants per round | `1` |
| `round_max_participants_count` | Maximum number of participants per round | `128` |
| `vtxo_min_amount` | Minimum allowed amount for vtxos | `-1` (dust) |
| `vtxo_max_amount` | Maximum allowed amount for vtxos | `-1` (unset) |
| `utxo_min_amount` | Minimum allowed amount for boarding or collaborative exit | `-1` (dust) |
| `utxo_max_amount` | Maximum allowed amount for boarding or collaborative exit | `-1` (unset) |
| `ban_duration` | Ban duration in seconds | `300` (5 minutes) |
| `ban_threshold` | Number of crimes to trigger a ban | `3` |
| `max_tx_weight` | Max transaction weight | `40000` |
| `settlement_min_expiry_gap` | Minimum gap between settlement and VTXO expiry in seconds | `0` |
| `vtxo_no_csv_validation_cutoff_date` | Unix timestamp after which CSV validation is enforced | `0` (disabled) |

## Provisioning

### Data Directory
Expand Down
216 changes: 216 additions & 0 deletions api-spec/openapi/swagger/ark/v1/admin.openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -833,6 +833,114 @@
}
}
},
"/v1/admin/settings": {
"get": {
"tags": [
"AdminService"
],
"operationId": "AdminService_GetSettings",
"responses": {
"200": {
"description": "a successful response.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/GetSettingsResponse"
}
}
}
},
"default": {
"description": "An unexpected error response.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Status"
}
}
}
}
}
},
"post": {
"tags": [
"AdminService"
],
"operationId": "AdminService_UpdateSettings",
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UpdateSettingsRequest"
}
}
},
"required": true
},
"responses": {
"200": {
"description": "a successful response.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UpdateSettingsResponse"
}
}
}
},
"default": {
"description": "An unexpected error response.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Status"
}
}
}
}
}
}
},
"/v1/admin/settings/clear": {
"post": {
"tags": [
"AdminService"
],
"operationId": "AdminService_ClearSettings",
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ClearSettingsRequest"
}
}
},
"required": true
},
"responses": {
"200": {
"description": "a successful response.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ClearSettingsResponse"
}
}
}
},
"default": {
"description": "An unexpected error response.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Status"
}
}
}
}
}
}
},
"/v1/admin/sweep": {
"post": {
"tags": [
Expand Down Expand Up @@ -953,6 +1061,14 @@
"title": "ClearScheduledSessionConfigResponse",
"type": "object"
},
"ClearSettingsRequest": {
"title": "ClearSettingsRequest",
"type": "object"
},
"ClearSettingsResponse": {
"title": "ClearSettingsResponse",
"type": "object"
},
"Conviction": {
"title": "Conviction",
"type": "object",
Expand Down Expand Up @@ -1335,6 +1451,19 @@
}
}
},
"GetSettingsRequest": {
"title": "GetSettingsRequest",
"type": "object"
},
"GetSettingsResponse": {
"title": "GetSettingsResponse",
"type": "object",
"properties": {
"settings": {
"$ref": "#/components/schemas/Settings"
}
}
},
"Intent": {
"title": "Intent",
"type": "object",
Expand Down Expand Up @@ -1563,6 +1692,80 @@
}
}
},
"Settings": {
"title": "Settings",
"type": "object",
"properties": {
"banDuration": {
"type": "integer",
"format": "int64"
},
"banThreshold": {
"type": "integer",
"format": "int64"
},
"boardingExitDelay": {
"type": "integer",
"format": "int64"
},
"checkpointExitDelay": {
"type": "integer",
"format": "int64"
},
"maxTxWeight": {
"type": "integer",
"format": "int64"
},
"publicUnilateralExitDelay": {
"type": "integer",
"format": "int64"
},
"roundMaxParticipantsCount": {
"type": "integer",
"format": "int64"
},
"roundMinParticipantsCount": {
"type": "integer",
"format": "int64"
},
"settlementMinExpiryGap": {
"type": "integer",
"format": "int64"
},
"unilateralExitDelay": {
"type": "integer",
"format": "int64"
},
"updatedAt": {
"type": "integer",
"format": "int64"
},
"utxoMaxAmount": {
"type": "integer",
"format": "int64"
},
"utxoMinAmount": {
"type": "integer",
"format": "int64"
},
"vtxoMaxAmount": {
"type": "integer",
"format": "int64"
},
"vtxoMinAmount": {
"type": "integer",
"format": "int64"
},
"vtxoNoCsvValidationCutoffDate": {
"type": "integer",
"format": "int64"
},
"vtxoTreeExpiry": {
"type": "integer",
"format": "int64"
}
}
},
Comment on lines +1839 to +1912
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

UpdateSettingsRequest does not match the handler’s real contract.

The schema makes settings optional, makes every nested field optional, and reuses the response model with writable-looking updatedAt. In internal/interface/grpc/handlers/adminservice.go, Lines 592-614, {} is rejected, omitted fields are read as zero and overwrite stored settings, and updatedAt is ignored. Please fix this at the proto/message level before regen so the published contract is either an explicit full-replace shape or a true patch shape.

Based on learnings, the project prioritizes backward compatibility for API endpoints.

Also applies to: 1861-1869

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@api-spec/openapi/swagger/ark/v1/admin.openapi.json` around lines 1695 - 1768,
The UpdateSettingsRequest/Settings schema must be updated at the proto/message
level to match the handler contract: decide whether the endpoint is a
full-replace or a patch and implement that explicitly—if full-replace, make
UpdateSettingsRequest.settings required and make each Settings field required
(remove writable updatedAt or mark updatedAt as output/readOnly); if patch,
change Settings fields to use nullable wrapper types (e.g.,
google.protobuf.Int64Value) or a FieldMask so omitted fields are distinguishable
from zero and won’t overwrite stored values; update the Settings message to not
expose updatedAt as an input field (mark it output-only) and then regen the
OpenAPI/JSON so UpdateSettingsRequest aligns with adminservice's behavior (refer
to UpdateSettingsRequest, Settings, and the adminservice handler logic).

"Status": {
"title": "Status",
"type": "object",
Expand Down Expand Up @@ -1654,6 +1857,19 @@
"UpdateScheduledSessionConfigResponse": {
"title": "UpdateScheduledSessionConfigResponse",
"type": "object"
},
"UpdateSettingsRequest": {
"title": "UpdateSettingsRequest",
"type": "object",
"properties": {
"settings": {
"$ref": "#/components/schemas/Settings"
}
}
},
"UpdateSettingsResponse": {
"title": "UpdateSettingsResponse",
"type": "object"
}
}
},
Expand Down
Loading
Loading