kitsune-kit 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +117 -2
- data/CONTRIBUTING.md +83 -0
- data/README.md +108 -136
- data/SECURITY.md +25 -0
- data/bin/kit +4 -1
- data/docs/architecture/decisions/0001-product-boundary.md +23 -0
- data/docs/architecture/decisions/0002-core-and-interfaces.md +23 -0
- data/docs/architecture/decisions/0003-configuration-state-and-secrets.md +23 -0
- data/docs/architecture/decisions/0004-supported-platforms.md +19 -0
- data/docs/architecture/decisions/0005-operation-semantics.md +19 -0
- data/docs/architecture/decisions/0006-tui-backend.md +21 -0
- data/docs/architecture/provider-adapters.md +49 -0
- data/docs/architecture.md +89 -0
- data/docs/commands.md +210 -0
- data/docs/configuration.md +210 -0
- data/docs/getting-started.md +133 -0
- data/docs/providers/digitalocean.md +52 -0
- data/docs/releasing.md +43 -0
- data/docs/roadmap.md +46 -0
- data/docs/security-audit.md +130 -0
- data/docs/security.md +116 -0
- data/docs/services/postgres.md +78 -0
- data/docs/services/redis.md +53 -0
- data/docs/testing.md +200 -0
- data/docs/troubleshooting.md +123 -0
- data/docs/tui.md +57 -0
- data/lib/kitsune/kit/adapters/confirming_host_key_verifier.rb +42 -0
- data/lib/kitsune/kit/adapters/digitalocean_provider.rb +217 -0
- data/lib/kitsune/kit/adapters/fake_clock.rb +34 -0
- data/lib/kitsune/kit/adapters/fake_provider.rb +107 -0
- data/lib/kitsune/kit/adapters/fake_reporter.rb +34 -0
- data/lib/kitsune/kit/adapters/fake_secret_store.rb +35 -0
- data/lib/kitsune/kit/adapters/fake_state_store.rb +91 -0
- data/lib/kitsune/kit/adapters/fake_transport.rb +56 -0
- data/lib/kitsune/kit/adapters/net_ssh_transport.rb +155 -0
- data/lib/kitsune/kit/adapters/provider.rb +34 -0
- data/lib/kitsune/kit/adapters/transport.rb +22 -0
- data/lib/kitsune/kit/adapters/transport_factory.rb +116 -0
- data/lib/kitsune/kit/application.rb +170 -0
- data/lib/kitsune/kit/cancellation.rb +21 -0
- data/lib/kitsune/kit/cli.rb +802 -64
- data/lib/kitsune/kit/clock.rb +11 -0
- data/lib/kitsune/kit/configuration.rb +493 -0
- data/lib/kitsune/kit/errors.rb +98 -0
- data/lib/kitsune/kit/events.rb +55 -0
- data/lib/kitsune/kit/operations/ensure_dns_records.rb +165 -0
- data/lib/kitsune/kit/operations/ensure_server.rb +135 -0
- data/lib/kitsune/kit/operations/ensure_service.rb +285 -0
- data/lib/kitsune/kit/operations/remote_script.rb +214 -0
- data/lib/kitsune/kit/operations/service_backup.rb +71 -0
- data/lib/kitsune/kit/operations/service_files.rb +127 -0
- data/lib/kitsune/kit/operations/service_firewall.rb +190 -0
- data/lib/kitsune/kit/operations/service_state.rb +59 -0
- data/lib/kitsune/kit/plan.rb +72 -0
- data/lib/kitsune/kit/reporters/human.rb +89 -0
- data/lib/kitsune/kit/reporters/json.rb +65 -0
- data/lib/kitsune/kit/reporters/reporter.rb +11 -0
- data/lib/kitsune/kit/result.rb +28 -0
- data/lib/kitsune/kit/run_journal.rb +102 -0
- data/lib/kitsune/kit/run_logger.rb +37 -0
- data/lib/kitsune/kit/scripts/docker.sh +137 -0
- data/lib/kitsune/kit/scripts/firewall.sh +205 -0
- data/lib/kitsune/kit/scripts/metrics.sh +54 -0
- data/lib/kitsune/kit/scripts/ssh.sh +95 -0
- data/lib/kitsune/kit/scripts/swap.sh +86 -0
- data/lib/kitsune/kit/scripts/unattended.sh +81 -0
- data/lib/kitsune/kit/scripts/user.sh +114 -0
- data/lib/kitsune/kit/secret_filter.rb +54 -0
- data/lib/kitsune/kit/secret_store.rb +32 -0
- data/lib/kitsune/kit/secret_stores/store.rb +12 -0
- data/lib/kitsune/kit/service_compose.rb +72 -0
- data/lib/kitsune/kit/state_store.rb +158 -0
- data/lib/kitsune/kit/state_stores/store.rb +15 -0
- data/lib/kitsune/kit/tui/actions.rb +72 -0
- data/lib/kitsune/kit/tui/application.rb +35 -0
- data/lib/kitsune/kit/tui/controller.rb +162 -0
- data/lib/kitsune/kit/tui/renderer.rb +134 -0
- data/lib/kitsune/kit/tui/state.rb +18 -0
- data/lib/kitsune/kit/tui/store.rb +88 -0
- data/lib/kitsune/kit/tui/terminal.rb +95 -0
- data/lib/kitsune/kit/version.rb +1 -1
- data/lib/kitsune/kit/workflows/apply_plan.rb +145 -0
- data/lib/kitsune/kit/workflows/base.rb +31 -0
- data/lib/kitsune/kit/workflows/build_plan.rb +33 -0
- data/lib/kitsune/kit/workflows/destroy_server.rb +84 -0
- data/lib/kitsune/kit/workflows/doctor.rb +225 -0
- data/lib/kitsune/kit/workflows/environment_selection.rb +70 -0
- data/lib/kitsune/kit/workflows/import_server.rb +100 -0
- data/lib/kitsune/kit/workflows/initialize_project.rb +121 -0
- data/lib/kitsune/kit/workflows/inspect_environment.rb +44 -0
- data/lib/kitsune/kit/workflows/rollback.rb +54 -0
- data/lib/kitsune/kit/workflows/support_bundle.rb +82 -0
- data/lib/kitsune/kit.rb +41 -2
- metadata +122 -79
- data/.rspec +0 -3
- data/Rakefile +0 -8
- data/kitsune-kit-logo.jpg +0 -0
- data/lib/kitsune/blueprints/.env.template +0 -31
- data/lib/kitsune/blueprints/docker/postgres.yml +0 -27
- data/lib/kitsune/blueprints/docker/redis.yml +0 -23
- data/lib/kitsune/blueprints/kit.env.template +0 -1
- data/lib/kitsune/kit/ansi_color.rb +0 -78
- data/lib/kitsune/kit/commands/bootstrap.rb +0 -134
- data/lib/kitsune/kit/commands/bootstrap_docker.rb +0 -66
- data/lib/kitsune/kit/commands/dns.rb +0 -112
- data/lib/kitsune/kit/commands/init.rb +0 -148
- data/lib/kitsune/kit/commands/install_docker_engine.rb +0 -146
- data/lib/kitsune/kit/commands/postinstall_docker.rb +0 -142
- data/lib/kitsune/kit/commands/provision.rb +0 -43
- data/lib/kitsune/kit/commands/setup_do_metrics.rb +0 -123
- data/lib/kitsune/kit/commands/setup_docker_prereqs.rb +0 -151
- data/lib/kitsune/kit/commands/setup_firewall.rb +0 -132
- data/lib/kitsune/kit/commands/setup_postgres_docker.rb +0 -246
- data/lib/kitsune/kit/commands/setup_redis_docker.rb +0 -241
- data/lib/kitsune/kit/commands/setup_swap.rb +0 -151
- data/lib/kitsune/kit/commands/setup_unattended.rb +0 -132
- data/lib/kitsune/kit/commands/setup_user.rb +0 -189
- data/lib/kitsune/kit/commands/ssh.rb +0 -46
- data/lib/kitsune/kit/commands/switch_env.rb +0 -42
- data/lib/kitsune/kit/defaults.rb +0 -91
- data/lib/kitsune/kit/env_loader.rb +0 -41
- data/lib/kitsune/kit/options_builder.rb +0 -26
- data/lib/kitsune/kit/provisioner.rb +0 -107
- data/sig/kitsune/kit.rbs +0 -6
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# ADR 0005: Plan, apply, rollback, remove and destroy semantics
|
|
2
|
+
|
|
3
|
+
- Status: accepted
|
|
4
|
+
- Date: 2026-08-14
|
|
5
|
+
|
|
6
|
+
## Decision
|
|
7
|
+
|
|
8
|
+
- `plan` observes and describes changes without mutation.
|
|
9
|
+
- `apply` converges actual state to desired state and verifies postconditions.
|
|
10
|
+
- `rollback` restores captured prior state when restoration is safe and supported.
|
|
11
|
+
- `remove` stops and removes a managed capability while preserving data.
|
|
12
|
+
- `destroy` permanently removes a provider resource.
|
|
13
|
+
- `destroy-data` permanently removes service data and is always separately confirmed.
|
|
14
|
+
|
|
15
|
+
Destructive operations show exact IDs and require the resource name as confirmation in an interactive terminal or an explicit confirmation option in non-interactive mode.
|
|
16
|
+
|
|
17
|
+
## Consequences
|
|
18
|
+
|
|
19
|
+
The word rollback is never used as a synonym for deleting a server or Docker volume. Operations that cannot restore prior state say so before execution.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# ADR 0006: Optional TUI backend
|
|
2
|
+
|
|
3
|
+
- Status: accepted
|
|
4
|
+
- Date: 2026-08-14
|
|
5
|
+
|
|
6
|
+
## Context
|
|
7
|
+
|
|
8
|
+
Kitsune Kit needs a full-screen terminal interface without making it necessary for automation or conventional CLI use. RatatuiRuby 1.5 provides excellent widgets and headless testing, but currently requires Ruby 3.2.9 or newer, distributes native platform artifacts, and is LGPL-3.0-or-later. Kitsune Kit currently supports the Ruby 3.2 series and must remain installable as one portable Ruby gem.
|
|
9
|
+
|
|
10
|
+
## Decision
|
|
11
|
+
|
|
12
|
+
- The domain emits presentation-neutral events.
|
|
13
|
+
- `Tui::Store`, `Tui::Renderer`, `Tui::Controller` and `Tui::Terminal` are separate components.
|
|
14
|
+
- The initial renderer uses a small ANSI terminal backend and a deterministic text buffer.
|
|
15
|
+
- Every TUI action invokes the same workflow used by the conventional CLI.
|
|
16
|
+
- A future RatatuiRuby backend may replace only renderer, input and terminal lifecycle components.
|
|
17
|
+
- The TUI remains optional at runtime; non-TTY use never initializes it.
|
|
18
|
+
|
|
19
|
+
## Consequences
|
|
20
|
+
|
|
21
|
+
The gem has no native TUI dependency and works completely through CLI/JSON. The initial widget set is intentionally smaller than RatatuiRuby, but its state, navigation, worker and snapshots remain reusable if a richer backend is adopted.
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# Provider and transport adapters
|
|
2
|
+
|
|
3
|
+
Kitsune Kit's adapter API version is `Kitsune::Kit::Adapters::API_VERSION`, currently `1`. It is a small internal extension boundary, not a dynamic plugin system. A breaking signature or semantic change must increment this value and the corresponding JSON/event schemas when affected.
|
|
4
|
+
|
|
5
|
+
## Provider contract
|
|
6
|
+
|
|
7
|
+
A provider subclasses `Kitsune::Kit::Adapters::Provider` and implements:
|
|
8
|
+
|
|
9
|
+
| Method | Contract |
|
|
10
|
+
| --- | --- |
|
|
11
|
+
| `validate_credentials!` | Return `true`, or raise a typed authentication/provider error. |
|
|
12
|
+
| `validate_server_spec!(spec:)` | Check region, size and image availability without creating anything. |
|
|
13
|
+
| `find_server(name:, tags:)` | Return an exact owned `ServerRecord` or `nil`; never adopt by name alone. |
|
|
14
|
+
| `find_server_by_id(id:)` | Return the exact record or `nil`. |
|
|
15
|
+
| `create_server(spec:)` | Create once and return the provider ID immediately. |
|
|
16
|
+
| `wait_until_ready(id:, timeout:)` | Return an active record with a public IP or raise `TimeoutError`. |
|
|
17
|
+
| `delete_server(id:)` | Delete by exact provider ID. |
|
|
18
|
+
| DNS methods | Find/upsert/delete records while preserving exact IDs and zones. |
|
|
19
|
+
|
|
20
|
+
Provider exceptions must be translated into `Kitsune::Kit::Errors` without copying tokens or raw provider response bodies into messages. Availability errors are configuration errors because the user can correct the desired spec; transient API failures remain retryable provider errors.
|
|
21
|
+
The CLI `--timeout` value configures both DropletKit open/read timeouts and the separate readiness deadline.
|
|
22
|
+
|
|
23
|
+
## Transport contract
|
|
24
|
+
|
|
25
|
+
A transport subclasses `Kitsune::Kit::Adapters::Transport`. `execute` receives a command and a separate argument array, applies a deadline, and returns `CommandResult` with independent `stdout`, `stderr`, `exit_status` and `duration_ms`. It does not infer success from output. `upload` accepts explicit content, absolute normalized path and restrictive mode. Host-key verification belongs to the connection adapter and must never silently disable verification.
|
|
26
|
+
|
|
27
|
+
## Verification
|
|
28
|
+
|
|
29
|
+
The same shared examples run against `FakeProvider`/`DigitalOceanProvider` and `FakeTransport`/`NetSshTransport`. They cover credentials, spec validation, lifecycle, DNS, reachability, result shape, uploads and timeout mapping. Adapter-specific tests cover provider error translation, SSH quoting, unsafe paths, host keys and failed uploads.
|
|
30
|
+
|
|
31
|
+
Run them with:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
bundle exec rspec spec/adapters spec/contracts
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Do not add a provider until it can pass these contracts and an opt-in real lifecycle test. The Hetzner
|
|
38
|
+
feasibility/sequencing review and the decision not to create a premature hook system are recorded in
|
|
39
|
+
[Roadmap and extension decisions](../roadmap.md).
|
|
40
|
+
|
|
41
|
+
The state boundary follows the same rule: `StateStores::Store` defines the port, while `StateStore` is the locked atomic filesystem adapter and `Adapters::FakeStateStore` is the deterministic in-memory adapter. Both execute `spec/contracts/state_store_contract.rb`.
|
|
42
|
+
|
|
43
|
+
`SecretStores::Store` and `Reporters::Reporter` are also explicit ports. Environment/fake secret stores and
|
|
44
|
+
human/JSON/fake reporters execute their shared contracts in `spec/adapters`; fakes make complete workflow tests
|
|
45
|
+
independent of the process environment and terminal.
|
|
46
|
+
|
|
47
|
+
`Clock` is the time port for workflow event timestamps, monotonic durations, journals and bounded waits.
|
|
48
|
+
`Adapters::FakeClock` advances deterministically without sleeping; both implementations execute
|
|
49
|
+
`spec/contracts/clock_contract.rb`.
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# Architecture
|
|
2
|
+
|
|
3
|
+
Kitsune Kit is a desired-state infrastructure tool with two replaceable presentation layers over one domain core.
|
|
4
|
+
|
|
5
|
+
```text
|
|
6
|
+
Conventional CLI ─┐
|
|
7
|
+
├─ workflows ─ operations ─ provider / SSH / state / secrets
|
|
8
|
+
Optional TUI ─────┘ │
|
|
9
|
+
└─ domain events ─ human / JSON / TUI / run log
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
Presentation code does not create provider clients, open SSH connections or implement infrastructure rules. CLI and TUI call the same workflow objects and consume the same event stream.
|
|
13
|
+
|
|
14
|
+
## Layers
|
|
15
|
+
|
|
16
|
+
### Configuration and domain values
|
|
17
|
+
|
|
18
|
+
`Configuration` loads/merges typed schema values and validates them before adapters are constructed. `Change`, `Plan`, `Result`, typed errors and versioned events are presentation-neutral values.
|
|
19
|
+
|
|
20
|
+
### Workflows
|
|
21
|
+
|
|
22
|
+
Workflows coordinate use cases:
|
|
23
|
+
|
|
24
|
+
- initialize/select an environment;
|
|
25
|
+
- build/apply/resume a plan;
|
|
26
|
+
- inspect/doctor/rollback/destroy;
|
|
27
|
+
- generate support diagnostics.
|
|
28
|
+
|
|
29
|
+
They emit events but do not print. Apply uses `RunJournal` to record a compatible plan and step status for safe resume.
|
|
30
|
+
|
|
31
|
+
The event vocabulary includes run/plan start and finish, operation start/progress/success/failure/skip, and warnings. All subscribers receive the same versioned event values; progress currently guarantees operation boundary percentages and can become finer-grained without changing presentations.
|
|
32
|
+
|
|
33
|
+
### Operations
|
|
34
|
+
|
|
35
|
+
Each operation exposes a non-mutating `plan`, idempotent `apply` and, where meaningful, ownership-aware `rollback`. Operations include server creation, versioned remote scripts, services and DNS.
|
|
36
|
+
|
|
37
|
+
An operation verifies its result before writing a managed marker/state. Destructive replacement appears in the plan and is never applied implicitly.
|
|
38
|
+
|
|
39
|
+
### Ports and adapters
|
|
40
|
+
|
|
41
|
+
- `Provider`: server-spec validation, exact server/DNS lookup, create/wait/delete and credential validation.
|
|
42
|
+
- `Transport`: reachability, command execution with separate output/status/timing, and safe upload.
|
|
43
|
+
- `StateStores::Store`: read/update/delete and per-environment mutation-lock port; filesystem and in-memory fake adapters share a contract.
|
|
44
|
+
- `SecretStores::Store`: resolves secret values without adding them to domain configuration/state; environment
|
|
45
|
+
and deterministic fake adapters execute the same contract.
|
|
46
|
+
- `Reporters::Reporter`: event-consumer port for human output, stable JSON, TUI state and redacted logs.
|
|
47
|
+
- `Clock`: UTC wall time, monotonic durations and sleeping, with a deterministic nonblocking fake.
|
|
48
|
+
|
|
49
|
+
Real DigitalOcean/Net::SSH adapters and deterministic fakes satisfy shared contracts. This permits full workflow failure injection without a VPS while reserving real behavior for integration/E2E suites.
|
|
50
|
+
|
|
51
|
+
The public adapter contract is versioned as `Kitsune::Kit::Adapters::API_VERSION`. See [Provider and transport adapters](architecture/provider-adapters.md).
|
|
52
|
+
|
|
53
|
+
## State and ownership
|
|
54
|
+
|
|
55
|
+
State schema version 1 contains environment, timestamps, resource identities, operation history and run journals. The server provider ID is recorded immediately after creation. DNS records are persisted one at a time. Remote resources carry script/config fingerprints and previous-state metadata.
|
|
56
|
+
|
|
57
|
+
State updates acquire a per-environment file lock, validate the whole document, preserve a backup, fsync a restricted temporary file and atomically rename it. Unsupported schemas/environment mismatches are rejected.
|
|
58
|
+
|
|
59
|
+
Names/tags help find resources, but provider IDs plus recorded ownership authorize destructive operations.
|
|
60
|
+
|
|
61
|
+
## Remote execution
|
|
62
|
+
|
|
63
|
+
Remote setup is stored in versioned Bash files under `lib/kitsune/kit/scripts`. `RemoteScript` fingerprints script bytes plus validated arguments, compares a remote marker, uploads through stdin-safe base64 transport, applies, verifies and records the marker.
|
|
64
|
+
|
|
65
|
+
SSH host verification is explicit. Commands and arguments are distinct at the transport boundary. Root and deploy transports are selected per operation; user bootstrap rollback deliberately uses root access.
|
|
66
|
+
|
|
67
|
+
Safety-critical SSH/firewall operations execute inside one preserved authenticated transport session while their postconditions are tested through separate fresh connections. The preserved session restores the captured policy if either fresh check fails.
|
|
68
|
+
|
|
69
|
+
## Services
|
|
70
|
+
|
|
71
|
+
`EnsureService` composes smaller collaborators for generated Compose YAML, file transaction/backups, firewall reconciliation, data backup and state transitions. Data lifecycle is deliberately separate from container/config lifecycle.
|
|
72
|
+
|
|
73
|
+
An enabled service in `external` mode is configuration metadata, not an operation. It is visible through the
|
|
74
|
+
CLI but excluded from VPS plans, Docker, firewall, doctor port checks and destructive lifecycle methods.
|
|
75
|
+
|
|
76
|
+
## TUI
|
|
77
|
+
|
|
78
|
+
The initial TUI is a pure-Ruby ANSI backend with deterministic renderer/store/controller boundaries. It has no native dependency and can be replaced without changing workflows. Headless tests feed events and keys into the store/controller and compare stable rendered buffers.
|
|
79
|
+
|
|
80
|
+
## Decisions
|
|
81
|
+
|
|
82
|
+
Detailed accepted decisions are in [architecture/decisions](architecture/decisions):
|
|
83
|
+
|
|
84
|
+
- product boundary and direct 0.5 rewrite;
|
|
85
|
+
- core/interface separation;
|
|
86
|
+
- configuration/state/secrets;
|
|
87
|
+
- supported platforms;
|
|
88
|
+
- operation semantics;
|
|
89
|
+
- optional TUI backend.
|
data/docs/commands.md
ADDED
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
# Command reference
|
|
2
|
+
|
|
3
|
+
The authoritative runtime reference is `kit help`, `kit help COMMAND` and the equivalent `kit COMMAND help`.
|
|
4
|
+
This document explains the semantics and automation contract that short help cannot capture.
|
|
5
|
+
|
|
6
|
+
## Global options
|
|
7
|
+
|
|
8
|
+
Global options may be passed to infrastructure commands:
|
|
9
|
+
|
|
10
|
+
| Option | Meaning |
|
|
11
|
+
| --- | --- |
|
|
12
|
+
| `-e`, `--env NAME` | Select an environment for this invocation. |
|
|
13
|
+
| `--root PATH` | Project root containing `.kitsune/`. Defaults to the current directory. |
|
|
14
|
+
| `--config PATH` | Use an alternative base configuration file. |
|
|
15
|
+
| `--format human|json` | Select human or versioned machine output. |
|
|
16
|
+
| `--no-color` | Disable ANSI styling. `NO_COLOR` is also respected by the entrypoint environment. |
|
|
17
|
+
| `--no-input` | Never prompt; fail with an actionable error when confirmation is required. |
|
|
18
|
+
| `--yes` | Approve reviewed, non-destructive changes. It never confirms permanent data/server destruction. |
|
|
19
|
+
| `--dry-run` | Build/display the plan without applying it. |
|
|
20
|
+
| `--quiet` | Limit human output to important findings and summaries. |
|
|
21
|
+
| `--verbose` | Include additional domain-event detail. |
|
|
22
|
+
| `--debug` | Include technical context/stack information for failures. |
|
|
23
|
+
| `--log` / `--no-log` | Enable/disable restricted redacted local logs. Enabled by default. |
|
|
24
|
+
| `--timeout SECONDS` | Upper bound for each remote step, provider HTTP request and readiness wait; must be positive. |
|
|
25
|
+
| `--trust-host-key SHA256:...` | Trust only this exact first-seen SSH fingerprint. |
|
|
26
|
+
|
|
27
|
+
## Core workflow
|
|
28
|
+
|
|
29
|
+
### `kit init [--force]`
|
|
30
|
+
|
|
31
|
+
Creates `.kitsune/config.yml`, a development overlay and environment selection. Existing generated files cause exit 9 unless `--force` is explicit. It also appends runtime artifacts to `.gitignore`.
|
|
32
|
+
|
|
33
|
+
### `kit doctor`
|
|
34
|
+
|
|
35
|
+
Read-only checks include Ruby/runtime, configuration, SSH-key permissions, security defaults, provider credentials, state schema, exact server ownership, verified SSH access, Ubuntu version, passwordless sudo, Docker/Compose, listening ports and managed drift.
|
|
36
|
+
|
|
37
|
+
Statuses are `pass`, `warn`, or `fail`. Warnings do not make the command fail; any failed check returns exit 1.
|
|
38
|
+
|
|
39
|
+
### `kit plan`
|
|
40
|
+
|
|
41
|
+
Calculates changes without mutation. Details exclude provider key IDs and secrets. Actions are `create`, `update`, `delete` and `no_change`; plans flag destructive changes.
|
|
42
|
+
|
|
43
|
+
### `kit apply`
|
|
44
|
+
|
|
45
|
+
Builds a fresh plan, displays it, rejects destructive replacement and asks for confirmation. `--yes --no-input` is the automation form. `--dry-run` guarantees no mutation.
|
|
46
|
+
|
|
47
|
+
Every run and step is journaled. The provider ID is saved immediately after server creation; remote markers and local state are saved after verified operations.
|
|
48
|
+
Human output ends with the redacted local log path and a post-apply `doctor`/zero-change-plan next step. Failed or
|
|
49
|
+
cancelled applies report the last confirmed step, run ID and exact `kit resume RUN_ID` command. JSON carries the
|
|
50
|
+
same guidance as a `warning_emitted` event and never receives an extra human-output line.
|
|
51
|
+
|
|
52
|
+
### `kit resume [RUN_ID]`
|
|
53
|
+
|
|
54
|
+
Resumes the named run or latest incomplete run. It rejects successful runs and saved operation sets that no longer match configuration. Completed steps are skipped. Confirmation is required (`--yes` in automation).
|
|
55
|
+
|
|
56
|
+
### `kit status`
|
|
57
|
+
|
|
58
|
+
Displays the selected environment, exact provider server and locally tracked resources. It does not run remote diagnostics; use `doctor` for that.
|
|
59
|
+
|
|
60
|
+
### `kit rollback`
|
|
61
|
+
|
|
62
|
+
Runs rollback in reverse dependency order for resources recorded as managed. It preserves the Droplet and service data volumes. It is not a universal snapshot restore: provider resources or pre-existing files that Kitsune Kit never owned are not touched.
|
|
63
|
+
|
|
64
|
+
## Server
|
|
65
|
+
|
|
66
|
+
```text
|
|
67
|
+
kit server show
|
|
68
|
+
kit server status
|
|
69
|
+
kit server create
|
|
70
|
+
kit server configure
|
|
71
|
+
kit server ssh
|
|
72
|
+
kit server import --provider-id DROPLET_ID --confirm-import SERVER_NAME
|
|
73
|
+
kit server destroy --confirm-destroy SERVER_NAME
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
- `show`/`status`: inspect server and state.
|
|
77
|
+
- `create`: apply only the server operation.
|
|
78
|
+
- `configure`: apply remote, service and DNS operations after the server operation.
|
|
79
|
+
- `ssh`: verify the managed connection, then replace the process with the system `ssh` client using strict known-host checking.
|
|
80
|
+
- `import`: recover only the server identity after state loss. It requires an exact numeric Droplet ID and configured server-name confirmation, then verifies name, tags, region, size, image, active status and public IP. It never imports remote-resource ownership.
|
|
81
|
+
- `destroy`: delete only the exact recorded provider ID after verifying name/tags, then restore/remove managed DNS and clear state. If provider deletion fails, DNS is left untouched; if DNS cleanup fails afterward, retrying completes cleanup without deleting a second server. It never deletes by name alone.
|
|
82
|
+
|
|
83
|
+
Interactive destruction asks the operator to type the exact server name. Automation must use `--confirm-destroy SERVER_NAME`; `--yes` is insufficient.
|
|
84
|
+
Before an interactive confirmation, Kitsune Kit prints the environment, provider, resource, recorded provider ID and recoverability. JSON confirmation errors include the same safe target context.
|
|
85
|
+
|
|
86
|
+
## Docker
|
|
87
|
+
|
|
88
|
+
```text
|
|
89
|
+
kit docker status
|
|
90
|
+
kit docker install
|
|
91
|
+
kit docker uninstall --yes
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Uninstall refuses to run while managed services remain. Docker installation uses the official apt repository and creates the `kitsune-private` network.
|
|
95
|
+
|
|
96
|
+
## Services
|
|
97
|
+
|
|
98
|
+
```text
|
|
99
|
+
kit service postgres status
|
|
100
|
+
kit service postgres install
|
|
101
|
+
kit service postgres backup
|
|
102
|
+
kit service postgres remove --yes
|
|
103
|
+
kit service postgres destroy-data --backup-before-destroy --confirm-destroy postgres@ENV
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Replace `postgres` with `redis` for Redis.
|
|
107
|
+
|
|
108
|
+
- `install`: service must be enabled and its secret environment variable present.
|
|
109
|
+
- `backup`: pauses the service, creates a restricted tar archive of the volume on the server and always unpauses it.
|
|
110
|
+
- `remove`: removes containers/network attachment and firewall rules owned by Kitsune Kit, but keeps the volume and managed files needed for recovery.
|
|
111
|
+
- `destroy-data`: optionally creates a restricted data archive, then removes containers, volume, owned
|
|
112
|
+
configuration backups/files and state. In an interactive terminal it offers the backup and requires typing
|
|
113
|
+
`TYPE@ENV`; automation can request it with `--backup-before-destroy` and must supply `--confirm-destroy`.
|
|
114
|
+
The data archive is retained outside the service directory, but must be copied to independent storage.
|
|
115
|
+
|
|
116
|
+
When an enabled service has `mode: external`, `status` returns only its configured host/port and
|
|
117
|
+
`managed: false`. All local lifecycle actions fail safely; Kitsune Kit never treats an external provider's data as
|
|
118
|
+
a VPS Docker volume.
|
|
119
|
+
|
|
120
|
+
## DNS
|
|
121
|
+
|
|
122
|
+
```text
|
|
123
|
+
kit dns list
|
|
124
|
+
kit dns plan
|
|
125
|
+
kit dns apply
|
|
126
|
+
kit dns remove --yes
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
DNS operations use exact record IDs and store original values. `remove` restores updated records and deletes only records created by Kitsune Kit.
|
|
130
|
+
|
|
131
|
+
## Environments
|
|
132
|
+
|
|
133
|
+
```text
|
|
134
|
+
kit env list
|
|
135
|
+
kit env current
|
|
136
|
+
kit env use NAME
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
`use` validates that `.kitsune/environments/NAME.yml` exists and writes the selection atomically. An explicit `--env`/`KITSUNE_ENV` wins without changing the persisted selection.
|
|
140
|
+
|
|
141
|
+
## Support
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
kit support bundle
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
The human-readable command prints the complete redacted bundle after writing it, so it can be reviewed before
|
|
148
|
+
sharing. JSON mode returns the local path without adding non-JSON output. Kitsune Kit never uploads the bundle.
|
|
149
|
+
|
|
150
|
+
Creates a permission-restricted JSON file under `.kitsune/support/` with versions, platform, redacted configuration/state, doctor results and selected redacted logs. The path is printed; Kitsune Kit never uploads it. Inspect it before sharing.
|
|
151
|
+
|
|
152
|
+
## TUI
|
|
153
|
+
|
|
154
|
+
`kit ui` starts the optional full-screen interface. Bare `kit` does the same only with interactive stdin/stdout; otherwise it prints help. See [TUI](tui.md).
|
|
155
|
+
|
|
156
|
+
## Version
|
|
157
|
+
|
|
158
|
+
`kit version`, `kit --version` and `kit -v` print the installed Kitsune Kit version without loading project configuration or contacting a provider.
|
|
159
|
+
|
|
160
|
+
## JSON output
|
|
161
|
+
|
|
162
|
+
Successful workflow documents use schema version 1 and include:
|
|
163
|
+
|
|
164
|
+
```json
|
|
165
|
+
{
|
|
166
|
+
"schema_version": 1,
|
|
167
|
+
"command": "plan",
|
|
168
|
+
"environment": "production",
|
|
169
|
+
"run_id": "...",
|
|
170
|
+
"status": "success",
|
|
171
|
+
"duration_ms": 12,
|
|
172
|
+
"result": {},
|
|
173
|
+
"warnings": [],
|
|
174
|
+
"events": []
|
|
175
|
+
}
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
Errors are JSON on standard error:
|
|
179
|
+
|
|
180
|
+
```json
|
|
181
|
+
{
|
|
182
|
+
"schema_version": 1,
|
|
183
|
+
"status": "failure",
|
|
184
|
+
"error": {
|
|
185
|
+
"code": "configuration_error",
|
|
186
|
+
"message": "...",
|
|
187
|
+
"hint": "...",
|
|
188
|
+
"context": {}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
Treat unknown additive keys as forward-compatible, but reject unsupported `schema_version` values in consumers.
|
|
194
|
+
|
|
195
|
+
## Exit codes
|
|
196
|
+
|
|
197
|
+
| Code | Meaning |
|
|
198
|
+
| ---: | --- |
|
|
199
|
+
| 0 | Success, including a no-change plan/apply. |
|
|
200
|
+
| 1 | Unexpected failure or failed `doctor` check. |
|
|
201
|
+
| 2 | Invalid CLI syntax/unknown command. |
|
|
202
|
+
| 3 | Invalid/missing configuration or unsupported environment. |
|
|
203
|
+
| 4 | Authentication failure. |
|
|
204
|
+
| 5 | Provider API failure. |
|
|
205
|
+
| 6 | SSH/network connection failure. |
|
|
206
|
+
| 7 | Remote command failure. |
|
|
207
|
+
| 8 | Post-change verification failure. |
|
|
208
|
+
| 9 | Unsafe operation or missing confirmation. |
|
|
209
|
+
| 10 | Timeout. |
|
|
210
|
+
| 130 | Interrupted by `SIGINT`. |
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
# Configuration
|
|
2
|
+
|
|
3
|
+
Kitsune Kit configuration schema version 1 is YAML with a fixed structure. Unknown keys at any nesting level,
|
|
4
|
+
malformed sections and future schema versions are rejected instead of being ignored.
|
|
5
|
+
|
|
6
|
+
## Files and precedence
|
|
7
|
+
|
|
8
|
+
The base file is `.kitsune/config.yml`. The selected overlay is `.kitsune/environments/NAME.yml`. Values are combined in this order, with later sources winning:
|
|
9
|
+
|
|
10
|
+
1. built-in safe defaults;
|
|
11
|
+
2. `.kitsune/config.yml`;
|
|
12
|
+
3. `.kitsune/environments/NAME.yml`;
|
|
13
|
+
4. supported environment-variable overrides;
|
|
14
|
+
5. explicit overrides supplied by the internal API.
|
|
15
|
+
|
|
16
|
+
Environment selection itself uses:
|
|
17
|
+
|
|
18
|
+
1. `--env NAME`;
|
|
19
|
+
2. `KITSUNE_ENV`;
|
|
20
|
+
3. `.kitsune/environment`;
|
|
21
|
+
4. `development`.
|
|
22
|
+
|
|
23
|
+
Names start with a lowercase letter or digit and may then contain lowercase letters, numbers, `_` and `-`; uppercase names and path traversal are rejected so Compose project identity remains stable.
|
|
24
|
+
|
|
25
|
+
Use:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
kit env list
|
|
29
|
+
kit env current
|
|
30
|
+
kit env use production
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Complete schema
|
|
34
|
+
|
|
35
|
+
```yaml
|
|
36
|
+
version: 1
|
|
37
|
+
|
|
38
|
+
provider:
|
|
39
|
+
name: digitalocean
|
|
40
|
+
token_env: DO_API_TOKEN
|
|
41
|
+
|
|
42
|
+
server:
|
|
43
|
+
name: myapp-production
|
|
44
|
+
region: sfo3
|
|
45
|
+
size: s-1vcpu-1gb
|
|
46
|
+
image: ubuntu-24-04-x64
|
|
47
|
+
ssh_key_id: "12345678"
|
|
48
|
+
tags:
|
|
49
|
+
- kitsune-managed
|
|
50
|
+
|
|
51
|
+
ssh:
|
|
52
|
+
user: deploy
|
|
53
|
+
port: 22
|
|
54
|
+
key_path: ~/.ssh/id_ed25519
|
|
55
|
+
allowed_cidrs:
|
|
56
|
+
- 203.0.113.10/32
|
|
57
|
+
|
|
58
|
+
services:
|
|
59
|
+
postgres:
|
|
60
|
+
enabled: false
|
|
61
|
+
mode: managed
|
|
62
|
+
host:
|
|
63
|
+
image: postgres:17
|
|
64
|
+
publish: false
|
|
65
|
+
bind: 127.0.0.1
|
|
66
|
+
allowed_cidrs: []
|
|
67
|
+
port: 5432
|
|
68
|
+
password_env: POSTGRES_PASSWORD
|
|
69
|
+
redis:
|
|
70
|
+
enabled: false
|
|
71
|
+
mode: managed
|
|
72
|
+
host:
|
|
73
|
+
image: redis:7.2
|
|
74
|
+
publish: false
|
|
75
|
+
bind: 127.0.0.1
|
|
76
|
+
allowed_cidrs: []
|
|
77
|
+
port: 6379
|
|
78
|
+
password_env: REDIS_PASSWORD
|
|
79
|
+
|
|
80
|
+
system:
|
|
81
|
+
swap_size_gb: 2
|
|
82
|
+
swap_swappiness: 10
|
|
83
|
+
unattended_upgrades: true
|
|
84
|
+
metrics: false
|
|
85
|
+
metrics_installer_sha256:
|
|
86
|
+
|
|
87
|
+
dns:
|
|
88
|
+
domains: []
|
|
89
|
+
ttl: 3600
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Field reference
|
|
93
|
+
|
|
94
|
+
### `provider`
|
|
95
|
+
|
|
96
|
+
| Field | Meaning |
|
|
97
|
+
| --- | --- |
|
|
98
|
+
| `name` | Must currently be `digitalocean`. |
|
|
99
|
+
| `token_env` | Name of the environment variable containing the token. Must look like an uppercase environment-variable name. |
|
|
100
|
+
|
|
101
|
+
### `server`
|
|
102
|
+
|
|
103
|
+
| Field | Validation |
|
|
104
|
+
| --- | --- |
|
|
105
|
+
| `name` | Lowercase DNS-style resource name, 1–63 characters. |
|
|
106
|
+
| `region` | Non-empty DigitalOcean region slug. Provider validity is checked by the API. |
|
|
107
|
+
| `size` | Non-empty DigitalOcean size slug. |
|
|
108
|
+
| `image` | `ubuntu-22-04-x64` or `ubuntu-24-04-x64`. |
|
|
109
|
+
| `ssh_key_id` | ID of a public key already uploaded to DigitalOcean. |
|
|
110
|
+
| `tags` | Tags used to establish ownership. Keep `kitsune-managed`; a same-name untagged server is not adopted. |
|
|
111
|
+
|
|
112
|
+
Region/size/image differences are immutable in the current model. A plan reports replacement as destructive; Kitsune Kit never silently replaces the server.
|
|
113
|
+
|
|
114
|
+
### `ssh`
|
|
115
|
+
|
|
116
|
+
| Field | Validation |
|
|
117
|
+
| --- | --- |
|
|
118
|
+
| `user` | Linux username: lowercase letters/numbers plus `_`/`-`, maximum 32 characters. |
|
|
119
|
+
| `port` | Integer from 1 through 65535. |
|
|
120
|
+
| `key_path` | Expanded local path to an existing regular private-key file with mode `0600` or stricter. |
|
|
121
|
+
| `allowed_cidrs` | Valid IPv4 or IPv6 CIDRs permitted through UFW. Empty means the SSH port is not CIDR-restricted by this list. |
|
|
122
|
+
|
|
123
|
+
SSH policy is ordered to keep a verified path open: create/verify the deploy user, install and validate the new policy, open the new firewall route, then remove obsolete managed rules.
|
|
124
|
+
|
|
125
|
+
### `services.postgres` and `services.redis`
|
|
126
|
+
|
|
127
|
+
| Field | Meaning |
|
|
128
|
+
| --- | --- |
|
|
129
|
+
| `enabled` | Include the service in the desired plan. |
|
|
130
|
+
| `mode` | `managed` installs on the VPS; `external` records an endpoint and forbids local lifecycle actions. |
|
|
131
|
+
| `host` | Required hostname/IP for `external`; must be empty for `managed`. |
|
|
132
|
+
| `image` | Valid Docker image reference. Pin a digest for stronger reproducibility. |
|
|
133
|
+
| `publish` | Publish a host port. Defaults to false. |
|
|
134
|
+
| `bind` | IPv4 bind address used only when publishing. |
|
|
135
|
+
| `allowed_cidrs` | Required and non-empty when `publish` is true. |
|
|
136
|
+
| `port` | Host port, 1–65535. Container ports remain 5432/6379. |
|
|
137
|
+
| `password_env` | Environment-variable name containing the required secret. |
|
|
138
|
+
|
|
139
|
+
Images, binds, ports and secret names reject newline/shell/YAML injection patterns before any SSH connection is opened.
|
|
140
|
+
|
|
141
|
+
External example:
|
|
142
|
+
|
|
143
|
+
```yaml
|
|
144
|
+
services:
|
|
145
|
+
postgres:
|
|
146
|
+
enabled: true
|
|
147
|
+
mode: external
|
|
148
|
+
host: db.internal.example
|
|
149
|
+
port: 5432
|
|
150
|
+
publish: false
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
External mode is metadata and a safety boundary, not a database-provisioning integration. `kit service postgres
|
|
154
|
+
status` reports the endpoint without its secret. Install, backup, remove and destroy-data refuse to act; manage
|
|
155
|
+
availability, TLS, backups and destruction with the external provider. External services never add Docker,
|
|
156
|
+
firewall or private-port checks to the VPS plan.
|
|
157
|
+
|
|
158
|
+
### `system`
|
|
159
|
+
|
|
160
|
+
| Field | Validation |
|
|
161
|
+
| --- | --- |
|
|
162
|
+
| `swap_size_gb` | Integer 0–64. Zero disables swap managed by Kitsune Kit. |
|
|
163
|
+
| `swap_swappiness` | Integer 0–100. |
|
|
164
|
+
| `unattended_upgrades` | Boolean. |
|
|
165
|
+
| `metrics` | Boolean; false by default. |
|
|
166
|
+
| `metrics_installer_sha256` | Required lowercase 64-character SHA256 when metrics is enabled. |
|
|
167
|
+
|
|
168
|
+
Kitsune Kit downloads the DigitalOcean metrics installer to a file, verifies the configured digest and only then executes it. Re-verify the digest whenever the upstream installer changes.
|
|
169
|
+
|
|
170
|
+
### `dns`
|
|
171
|
+
|
|
172
|
+
| Field | Validation |
|
|
173
|
+
| --- | --- |
|
|
174
|
+
| `domains` | Valid hostnames. Public Suffix List parsing determines the provider zone and record name. |
|
|
175
|
+
| `ttl` | Integer 30–86400 seconds. |
|
|
176
|
+
|
|
177
|
+
Each created/updated record is persisted immediately, including its previous value, so a partially failed run can resume and rollback safely.
|
|
178
|
+
|
|
179
|
+
## Supported environment-variable overrides
|
|
180
|
+
|
|
181
|
+
| Variable | Field |
|
|
182
|
+
| --- | --- |
|
|
183
|
+
| `KITSUNE_PROVIDER` | `provider.name` |
|
|
184
|
+
| `KITSUNE_SERVER_NAME` | `server.name` |
|
|
185
|
+
| `KITSUNE_REGION` | `server.region` |
|
|
186
|
+
| `KITSUNE_SIZE` | `server.size` |
|
|
187
|
+
| `KITSUNE_IMAGE` | `server.image` |
|
|
188
|
+
| `KITSUNE_SSH_KEY_ID` | `server.ssh_key_id` |
|
|
189
|
+
| `KITSUNE_SSH_USER` | `ssh.user` |
|
|
190
|
+
| `KITSUNE_SSH_PORT` | `ssh.port` |
|
|
191
|
+
| `KITSUNE_SSH_KEY_PATH` | `ssh.key_path` |
|
|
192
|
+
| `KITSUNE_METRICS_INSTALLER_SHA256` | `system.metrics_installer_sha256` |
|
|
193
|
+
|
|
194
|
+
Secrets are indirect: `provider.token_env` and each `password_env` name which environment variable Kitsune Kit reads. Their values are registered with the redactor and never written to state.
|
|
195
|
+
Enabled services require at least 12 bytes and reject empty values and common defaults such as `password`, `postgres`, `redis`, `changeme` and `secret` before any provider or SSH request.
|
|
196
|
+
|
|
197
|
+
## Schema evolution
|
|
198
|
+
|
|
199
|
+
Version 1 is the direct-rewrite baseline and has no supported predecessor to migrate: the old preview `.env`
|
|
200
|
+
format is intentionally not interpreted. A future schema change must ship an explicit, tested `N -> N+1`
|
|
201
|
+
migrator that preserves a backup before changing configuration or state. Kitsune Kit never guesses at an unknown
|
|
202
|
+
version. Until such a migrator exists, open the project with a compatible Kitsune Kit version and follow that
|
|
203
|
+
release's documented migration path; never copy provider IDs by hand.
|
|
204
|
+
|
|
205
|
+
JSON/event consumers must reject unknown `schema_version` values while tolerating documented additive fields
|
|
206
|
+
within a supported version. Breaking adapter signatures increment `Adapters::API_VERSION`.
|
|
207
|
+
|
|
208
|
+
## State is not configuration
|
|
209
|
+
|
|
210
|
+
`.kitsune/state/ENV.json` is Kitsune Kit's ownership journal. Do not edit it manually or commit it. Writes use a per-environment lock, atomic rename and `.backup` copy. Losing state removes Kitsune Kit's proof of ownership; it intentionally refuses destructive actions rather than guessing by name.
|