Skip to content

Commit 9a0f14b

Browse files
committed
feat: deliver v1.2.0 tenant security and release improvements
1 parent 8a2511f commit 9a0f14b

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+2237
-265
lines changed

AGENTS.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ Connectors: vmware/ (govmomi), proxmox/ (REST), hyperv/ (WinRM/PowerShell), kvm/
2020
- Backup-oriented connectors such as `internal/connectors/veeam/` may expose domain-specific discovery methods instead of the core VM discovery interface when they enrich the universal inventory rather than replace it.
2121
- The KVM connector supports a real libvirt-backed implementation behind the `libvirt` build tag; the default build keeps an XML-backed fallback so the repo remains portable on machines without libvirt development tooling.
2222
- Community plugins are loaded through `internal/connectors/plugin/`, which exposes a gRPC-based plugin host and a sample plugin under `examples/plugin-example/`.
23+
- Plugin manifests may optionally declare `minimum_viaduct_version` and `maximum_viaduct_version` so host/plugin compatibility stays explicit once releases are published.
2324
## Code Standards
2425
- Run golangci-lint before committing. Config in .golangci.yml.
2526
- All exported functions require doc comments.
@@ -116,12 +117,16 @@ Scopes: cli, discovery, vmware, proxmox, hyperv, kvm, nutanix, migrate, deps, li
116117
- Keep operator guidance honest about maturity. If a workflow is backend-only or automation-oriented rather than a first-class CLI command, document it that way instead of implying a polished surface that does not exist.
117118
- Release verification should exercise packaging as well as builds and tests. If packaging breaks, the release is not ready even if `go build` passes.
118119
- Plugin executable launches now expect a `plugin.json` sidecar manifest with name, platform, version, and protocol version metadata. Keep the manifest aligned with the plugin’s reported platform.
120+
- Release bundles now include a machine-readable `dependency-manifest.json` alongside the release manifest and checksums. Treat it as part of the public artifact contract.
119121
- Tenant-scoped audit, reporting, and request-correlation behavior are part of the operator surface. Changes to those routes should update both the API docs and troubleshooting guidance.
122+
- Tenant automation should prefer service accounts over shared tenant API keys. Viewer, operator, and admin roles are part of the tenant isolation model and should stay explicit in API routing.
123+
- The API route `/api/v1/about` is the canonical operator-visible build metadata surface for packaged environments and should stay aligned with CLI version output and release manifests.
124+
- Migration `credential_ref` handling is operator-visible behavior. If it changes, update `configs/config.example.yaml` and the configuration docs in the same change.
120125
- Treat install, upgrade, and rollback documentation as operational code. Broken runbooks are production bugs.
121126
## Current State
122127
- Discovery is implemented for VMware, Proxmox, Hyper-V, KVM, and Nutanix, with Veeam available for backup and restore-point enrichment plus portability planning.
123128
- `internal/migrate/` includes declarative spec parsing, workload matching, pre-flight checks, execution windows, approval gates, wave planning, resumable checkpoints, disk conversion, cold and warm migration orchestration, replication progress tracking, boot verification, cutover coordination, and rollback support.
124129
- `internal/lifecycle/` provides cost modeling, policy evaluation/simulation, waiver-aware remediation guidance, and drift detection backed by sample cost profiles and policy definitions under `configs/`.
125-
- `internal/store/` persists discovery snapshots, migration state, recovery points, and tenant metadata in both in-memory and PostgreSQL backends.
130+
- `internal/store/` persists discovery snapshots, migration state, recovery points, and tenant metadata in both in-memory and PostgreSQL backends, including tenant quotas and service-account definitions.
126131
- The Go API server and React dashboard are both present, and `web/` builds into production static assets with inventory, migration, history, dependency-graph, tenant summary, runbook, and lifecycle remediation views.
127132
- Integration coverage includes discovery, cold migration, scheduled-window pre-flight gating, migration resume after interruption, warm migration resume, cutover rollback on boot failure, tenant isolation under concurrent access, lifecycle recommendation/simulation flows, plugin crash handling, and backup portability workflows.

CHANGELOG.md

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,28 @@ All notable changes to Viaduct should be documented in this file.
44

55
This changelog tracks published releases and the major implementation milestones that shaped the current repository state.
66

7-
## [1.0.0] - 2026-04-05
8-
9-
### Highlights
10-
- shipped the first tagged Viaduct release with a release-gated CLI, API, dashboard, install scripts, packaged web assets, checksums, and release manifest generation
11-
- delivered multi-platform discovery for VMware, Proxmox, Hyper-V, KVM, Nutanix, and Veeam-related backup inventory
12-
- delivered dependency-aware migration planning, cold and warm migration workflows, execution windows, approval gates, checkpoints, resume support, verification, and rollback
13-
- delivered lifecycle cost, policy, drift, remediation, and simulation workflows
14-
- delivered tenant-scoped API access, persistent state backends, plugin hosting, contributor docs, operator runbooks, and example lab environments
15-
167
## Unreleased
178

189
- no unreleased changes currently tracked
1910

11+
## [1.2.0] - 2026-04-07
12+
13+
### Tenant Security And Scale
14+
- added tenant-scoped service accounts with viewer, operator, and admin roles for API authentication
15+
- added role-gated tenant routes and a current-tenant introspection route without leaking API keys
16+
- added tenant quotas for API request rate, snapshot count, and migration count
17+
18+
### Migration And API Correctness
19+
- fixed current-inventory aggregation so the API no longer misses sources once snapshot history grows past twenty entries
20+
- replaced brittle pending-approval summary detection with real migration-state decoding
21+
- wired migration `credential_ref` resolution through the CLI config and API server connector-resolution paths
22+
- added the `/api/v1/about` route for operator-visible build and compatibility metadata
23+
24+
### Plugin And Release Operability
25+
- added optional plugin host-version compatibility markers in `plugin.json`
26+
- added a machine-readable `dependency-manifest.json` to packaged release bundles
27+
- expanded regression coverage for service-account auth, quota enforcement, plugin compatibility, packaging metadata, and summary correctness
28+
2029
## [1.1.0] - 2026-04-05
2130

2231
### Current Stable Surface
@@ -43,6 +52,15 @@ This changelog tracks published releases and the major implementation milestones
4352
- added release-era install, quickstart, upgrade, release, support, and troubleshooting entrypoints
4453
- improved directory onboarding for docs, configs, examples, API assets, tests, and the dashboard
4554

55+
## [1.0.0] - 2026-04-05
56+
57+
### Highlights
58+
- shipped the first tagged Viaduct release with a release-gated CLI, API, dashboard, install scripts, packaged web assets, checksums, and release manifest generation
59+
- delivered multi-platform discovery for VMware, Proxmox, Hyper-V, KVM, Nutanix, and Veeam-related backup inventory
60+
- delivered dependency-aware migration planning, cold and warm migration workflows, execution windows, approval gates, checkpoints, resume support, verification, and rollback
61+
- delivered lifecycle cost, policy, drift, remediation, and simulation workflows
62+
- delivered tenant-scoped API access, persistent state backends, plugin hosting, contributor docs, operator runbooks, and example lab environments
63+
4664
## Historical Implementation Milestones
4765

4866
### Phase 4 Complete

INSTALL.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ Release bundles produced by `make package-release-matrix` include:
3333
- built web assets
3434
- docs and sample configs
3535
- install scripts
36-
- checksums and a release manifest
36+
- checksums, a release manifest, and a dependency manifest
3737

3838
On POSIX systems:
3939

QUICKSTART.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ The sample config already points the KVM source at the local lab fixtures.
3535

3636
```bash
3737
./bin/viaduct serve-api --port 8080
38+
curl http://localhost:8080/api/v1/about
3839
```
3940

4041
## 6. Start The Dashboard
@@ -47,6 +48,12 @@ npm run dev
4748

4849
The dashboard expects the API at `/api` and can use `VITE_VIADUCT_API_KEY` from [web/.env.example](web/.env.example) when tenant-scoped access is enabled.
4950

51+
If you create additional tenants or service accounts, use:
52+
53+
```bash
54+
curl -H "X-API-Key: <tenant-key>" http://localhost:8080/api/v1/tenants/current
55+
```
56+
5057
## Next Steps
5158
- Review [docs/operations/migration-operations.md](docs/operations/migration-operations.md) for execution and rollback workflows.
5259
- Review [docs/operations/backup-portability.md](docs/operations/backup-portability.md) for Veeam portability guidance.

README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,14 @@ Viaduct is an open source control plane for discovering, migrating, and operatin
1010
Broadcom's VMware licensing changes forced many teams into urgent platform decisions, but most migration tooling still assumes a one-time move into a single destination. Viaduct is built for operators who need a durable mixed-platform operating model: discover what exists, understand the blast radius, move workloads safely, preserve backup coverage, and keep managing cost, policy, and drift after cutover.
1111

1212
## Project Status
13-
Viaduct is ready for broad evaluation, operator pilots, and community contribution. The repository includes multi-platform discovery, dependency graphing, declarative migration orchestration, warm-migration primitives, lifecycle remediation, backup portability, multi-tenancy, plugin hosting, a web dashboard, reproducible release packaging, and a shared release gate for CI and local verification.
13+
Viaduct is ready for broad evaluation, operator pilots, and community contribution. The repository includes multi-platform discovery, dependency graphing, declarative migration orchestration, warm-migration primitives, lifecycle remediation, backup portability, multi-tenancy with service accounts and quota controls, plugin hosting, a web dashboard, reproducible release packaging, and a shared release gate for CI and local verification.
1414

1515
## Supported Capabilities
1616
- Discovery engine: Collects normalized inventory from VMware, Proxmox, Hyper-V, KVM, Nutanix, and Veeam-related backup systems into a universal schema.
1717
- Dependency mapping: Builds graph views across workloads, networks, storage, and backup relationships to support safer migration planning.
1818
- Migration orchestration: Supports declarative planning, preflight validation, cold and warm migration flows, execution windows, approval gates, checkpoints, resume support, and rollback.
1919
- Lifecycle analysis: Evaluates cost, policy, and drift, then turns those signals into remediation guidance and simulation output.
20-
- Multi-tenancy and extensibility: Provides tenant-scoped API access, persistent state backends, and a gRPC-based plugin host for community connectors.
20+
- Multi-tenancy and extensibility: Provides tenant-scoped API access, service-account and role-based access controls, persistent state backends, and a gRPC-based plugin host for community connectors.
2121
- Operator surfaces: Exposes the same core workflows through a CLI, REST API, and React dashboard.
2222

2323
## Supported Connectors And Integrations
@@ -49,6 +49,7 @@ make build
4949
./bin/viaduct discover --type kvm --source examples/lab/kvm --save
5050
./bin/viaduct plan --spec examples/lab/migration-window.yaml
5151
./bin/viaduct serve-api --port 8080
52+
curl http://localhost:8080/api/v1/about
5253

5354
cd web
5455
npm ci

RELEASE.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ This document describes the stable packaging and release process for Viaduct.
1212
1. Ensure the working tree is in the intended state and public docs are current.
1313
2. Run `make release-gate`.
1414
3. Inspect the generated bundles in `dist/`.
15-
4. Verify `release-manifest.json` and `SHA256SUMS.txt`.
15+
4. Verify `release-manifest.json`, `dependency-manifest.json`, and `SHA256SUMS.txt`.
1616
5. Smoke-test the packaged binary with `viaduct version` and `viaduct --help`.
1717
6. Confirm install docs, upgrade docs, and rollback docs still match the artifact layout.
1818
7. Tag and publish only after the verification and smoke checks are clean.
@@ -25,7 +25,7 @@ The release bundle should include:
2525
- docs
2626
- sample configs
2727
- examples
28-
- manifest and checksums
28+
- release manifest, dependency manifest, and checksums
2929
- deployment reference assets
3030

3131
## Release Notes Guidance

cmd/viaduct/config.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ type appConfig struct {
1616
Insecure bool `yaml:"insecure"`
1717
StateStoreDSN string `yaml:"state_store_dsn"`
1818
Sources map[string]connectors.Config `yaml:"sources"`
19+
Credentials map[string]connectors.Config `yaml:"credentials"`
1920
Plugins map[string]string `yaml:"plugins"`
2021
}
2122

@@ -108,6 +109,34 @@ func mergeConnectorConfig(target *connectors.Config, source connectors.Config) {
108109
}
109110
}
110111

112+
func mergeConnectorCredentialConfig(target *connectors.Config, source connectors.Config) {
113+
if target.Username == "" && source.Username != "" {
114+
target.Username = source.Username
115+
}
116+
if target.Password == "" && source.Password != "" {
117+
target.Password = source.Password
118+
}
119+
if !target.Insecure && source.Insecure {
120+
target.Insecure = true
121+
}
122+
if target.Port == 0 && source.Port != 0 {
123+
target.Port = source.Port
124+
}
125+
}
126+
127+
func resolveMigrationConnectorConfig(address, platform, credentialRef string, cfg *appConfig) connectors.Config {
128+
config := resolveConnectorConfig(address, platform, "", "", false, cfg)
129+
if cfg == nil {
130+
return config
131+
}
132+
133+
if credentialConfig, ok := cfg.Credentials[strings.TrimSpace(credentialRef)]; ok {
134+
mergeConnectorCredentialConfig(&config, credentialConfig)
135+
}
136+
config.Address = address
137+
return config
138+
}
139+
111140
func expandPath(path string) (string, error) {
112141
if path == "" {
113142
return path, nil

cmd/viaduct/config_test.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ package main
33
import (
44
"path/filepath"
55
"testing"
6+
7+
"github.com/eblackrps/viaduct/internal/connectors"
68
)
79

810
func TestLoadAppConfig_ExampleConfig_Parses(t *testing.T) {
@@ -23,7 +25,38 @@ func TestLoadAppConfig_ExampleConfig_Parses(t *testing.T) {
2325
if cfg.Sources["kvm"].Address != "examples/lab/kvm" {
2426
t.Fatalf("kvm source address = %q, want examples/lab/kvm", cfg.Sources["kvm"].Address)
2527
}
28+
if cfg.Credentials["source/vcenter"].Username == "" {
29+
t.Fatal("source/vcenter credential username = empty, want populated example credential")
30+
}
2631
if cfg.Plugins["example"] != "grpc://127.0.0.1:50071" {
2732
t.Fatalf("example plugin address = %q, want grpc://127.0.0.1:50071", cfg.Plugins["example"])
2833
}
2934
}
35+
36+
func TestResolveMigrationConnectorConfig_CredentialRefApplied_Expected(t *testing.T) {
37+
t.Parallel()
38+
39+
cfg := &appConfig{
40+
Sources: map[string]connectors.Config{
41+
"vmware": {Insecure: true},
42+
},
43+
Credentials: map[string]connectors.Config{
44+
"source/vcenter": {
45+
Username: "administrator@vsphere.local",
46+
Password: "secret",
47+
Port: 443,
48+
},
49+
},
50+
}
51+
52+
config := resolveMigrationConnectorConfig("https://vcsa.lab.local", "vmware", "source/vcenter", cfg)
53+
if config.Address != "https://vcsa.lab.local" {
54+
t.Fatalf("Address = %q, want https://vcsa.lab.local", config.Address)
55+
}
56+
if config.Username != "administrator@vsphere.local" || config.Password != "secret" {
57+
t.Fatalf("credentials = %#v, want credential-ref values", config)
58+
}
59+
if !config.Insecure || config.Port != 443 {
60+
t.Fatalf("transport settings = %#v, want insecure source config and port 443", config)
61+
}
62+
}

cmd/viaduct/main.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ var (
3737
)
3838

3939
func init() {
40+
_ = os.Setenv("VIADUCT_VERSION", version)
41+
4042
rootCmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "Enable verbose output")
4143
rootCmd.PersistentFlags().StringVarP(&output, "output", "o", "table", "Output format: table, json, yaml")
4244
rootCmd.PersistentFlags().StringVarP(&configPath, "config", "c", "~/.viaduct/config.yaml", "Path to config file")

cmd/viaduct/migration_helpers.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ import (
99
)
1010

1111
func connectorsForSpec(cfg *appConfig, catalog *connectorcatalog.Catalog, spec *migratepkg.MigrationSpec) (sourceConnector connectors.Connector, targetConnector connectors.Connector, err error) {
12-
sourceConfig := resolveConnectorConfig(spec.Source.Address, string(spec.Source.Platform), "", "", false, cfg)
13-
targetConfig := resolveConnectorConfig(spec.Target.Address, string(spec.Target.Platform), "", "", false, cfg)
12+
sourceConfig := resolveMigrationConnectorConfig(spec.Source.Address, string(spec.Source.Platform), spec.Source.CredentialRef, cfg)
13+
targetConfig := resolveMigrationConnectorConfig(spec.Target.Address, string(spec.Target.Platform), spec.Target.CredentialRef, cfg)
1414

1515
sourceConnector, err = catalog.Build(spec.Source.Platform, sourceConfig)
1616
if err != nil {

0 commit comments

Comments
 (0)