nvoi 0.1.5

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.
Files changed (178) hide show
  1. checksums.yaml +7 -0
  2. data/.rubocop.yml +19 -0
  3. data/Gemfile +9 -0
  4. data/Gemfile.lock +151 -0
  5. data/Makefile +26 -0
  6. data/Rakefile +16 -0
  7. data/doc/config-schema.yaml +357 -0
  8. data/examples/apex-wildcard/deploy.yml +68 -0
  9. data/examples/golang/.gitignore +19 -0
  10. data/examples/golang/Dockerfile +43 -0
  11. data/examples/golang/README.md +59 -0
  12. data/examples/golang/deploy.enc +0 -0
  13. data/examples/golang/deploy.yml +54 -0
  14. data/examples/golang/go.mod +39 -0
  15. data/examples/golang/go.sum +96 -0
  16. data/examples/golang/main.go +177 -0
  17. data/examples/golang/models/user.go +17 -0
  18. data/examples/golang-postgres-multi/.gitignore +18 -0
  19. data/examples/golang-postgres-multi/Dockerfile +39 -0
  20. data/examples/golang-postgres-multi/README.md +211 -0
  21. data/examples/golang-postgres-multi/deploy.yml +67 -0
  22. data/examples/golang-postgres-multi/go.mod +45 -0
  23. data/examples/golang-postgres-multi/go.sum +108 -0
  24. data/examples/golang-postgres-multi/main.go +197 -0
  25. data/examples/golang-postgres-multi/models/user.go +17 -0
  26. data/examples/postgres-multi/.env.production.example +11 -0
  27. data/examples/postgres-multi/README.md +112 -0
  28. data/examples/postgres-multi/deploy.yml +74 -0
  29. data/examples/postgres-single/.env.production.example +11 -0
  30. data/examples/postgres-single/.gitignore +15 -0
  31. data/examples/postgres-single/Dockerfile +35 -0
  32. data/examples/postgres-single/README.md +76 -0
  33. data/examples/postgres-single/deploy.yml +56 -0
  34. data/examples/postgres-single/go.mod +45 -0
  35. data/examples/postgres-single/go.sum +108 -0
  36. data/examples/postgres-single/main.go +184 -0
  37. data/examples/rails-single/.dockerignore +51 -0
  38. data/examples/rails-single/.env.production.example +11 -0
  39. data/examples/rails-single/.github/dependabot.yml +12 -0
  40. data/examples/rails-single/.github/workflows/ci.yml +39 -0
  41. data/examples/rails-single/.gitignore +20 -0
  42. data/examples/rails-single/.node-version +1 -0
  43. data/examples/rails-single/.rubocop.yml +8 -0
  44. data/examples/rails-single/.ruby-version +1 -0
  45. data/examples/rails-single/Dockerfile +86 -0
  46. data/examples/rails-single/Gemfile +56 -0
  47. data/examples/rails-single/Gemfile.lock +350 -0
  48. data/examples/rails-single/Procfile.dev +3 -0
  49. data/examples/rails-single/README.md +17 -0
  50. data/examples/rails-single/Rakefile +6 -0
  51. data/examples/rails-single/app/assets/builds/.keep +0 -0
  52. data/examples/rails-single/app/assets/images/.keep +0 -0
  53. data/examples/rails-single/app/assets/stylesheets/application.tailwind.css +1 -0
  54. data/examples/rails-single/app/controllers/application_controller.rb +4 -0
  55. data/examples/rails-single/app/controllers/concerns/.keep +0 -0
  56. data/examples/rails-single/app/controllers/users_controller.rb +19 -0
  57. data/examples/rails-single/app/helpers/application_helper.rb +2 -0
  58. data/examples/rails-single/app/javascript/application.js +3 -0
  59. data/examples/rails-single/app/javascript/controllers/application.js +9 -0
  60. data/examples/rails-single/app/javascript/controllers/hello_controller.js +7 -0
  61. data/examples/rails-single/app/javascript/controllers/index.js +8 -0
  62. data/examples/rails-single/app/jobs/application_job.rb +7 -0
  63. data/examples/rails-single/app/mailers/application_mailer.rb +4 -0
  64. data/examples/rails-single/app/models/application_record.rb +3 -0
  65. data/examples/rails-single/app/models/concerns/.keep +0 -0
  66. data/examples/rails-single/app/models/user.rb +2 -0
  67. data/examples/rails-single/app/views/layouts/application.html.erb +28 -0
  68. data/examples/rails-single/app/views/layouts/mailer.html.erb +13 -0
  69. data/examples/rails-single/app/views/layouts/mailer.text.erb +1 -0
  70. data/examples/rails-single/app/views/pwa/manifest.json.erb +22 -0
  71. data/examples/rails-single/app/views/pwa/service-worker.js +26 -0
  72. data/examples/rails-single/app/views/users/index.html.erb +38 -0
  73. data/examples/rails-single/bin/brakeman +7 -0
  74. data/examples/rails-single/bin/bundle +109 -0
  75. data/examples/rails-single/bin/dev +11 -0
  76. data/examples/rails-single/bin/docker-entrypoint +14 -0
  77. data/examples/rails-single/bin/jobs +6 -0
  78. data/examples/rails-single/bin/kamal +27 -0
  79. data/examples/rails-single/bin/rails +4 -0
  80. data/examples/rails-single/bin/rake +4 -0
  81. data/examples/rails-single/bin/rubocop +8 -0
  82. data/examples/rails-single/bin/setup +37 -0
  83. data/examples/rails-single/bin/thrust +5 -0
  84. data/examples/rails-single/bun.lock +224 -0
  85. data/examples/rails-single/config/application.rb +42 -0
  86. data/examples/rails-single/config/boot.rb +4 -0
  87. data/examples/rails-single/config/cable.yml +17 -0
  88. data/examples/rails-single/config/cache.yml +16 -0
  89. data/examples/rails-single/config/credentials.yml.enc +1 -0
  90. data/examples/rails-single/config/database.yml +100 -0
  91. data/examples/rails-single/config/environment.rb +5 -0
  92. data/examples/rails-single/config/environments/development.rb +69 -0
  93. data/examples/rails-single/config/environments/production.rb +87 -0
  94. data/examples/rails-single/config/environments/test.rb +50 -0
  95. data/examples/rails-single/config/initializers/assets.rb +7 -0
  96. data/examples/rails-single/config/initializers/content_security_policy.rb +25 -0
  97. data/examples/rails-single/config/initializers/filter_parameter_logging.rb +8 -0
  98. data/examples/rails-single/config/initializers/inflections.rb +16 -0
  99. data/examples/rails-single/config/locales/en.yml +31 -0
  100. data/examples/rails-single/config/puma.rb +41 -0
  101. data/examples/rails-single/config/queue.yml +18 -0
  102. data/examples/rails-single/config/recurring.yml +15 -0
  103. data/examples/rails-single/config/routes.rb +4 -0
  104. data/examples/rails-single/config.ru +6 -0
  105. data/examples/rails-single/db/cable_schema.rb +11 -0
  106. data/examples/rails-single/db/cache_schema.rb +12 -0
  107. data/examples/rails-single/db/migrate/20251123095526_create_users.rb +10 -0
  108. data/examples/rails-single/db/queue_schema.rb +129 -0
  109. data/examples/rails-single/db/seeds.rb +9 -0
  110. data/examples/rails-single/deploy.yml +57 -0
  111. data/examples/rails-single/lib/tasks/.keep +0 -0
  112. data/examples/rails-single/log/.keep +0 -0
  113. data/examples/rails-single/package.json +17 -0
  114. data/examples/rails-single/public/400.html +114 -0
  115. data/examples/rails-single/public/404.html +114 -0
  116. data/examples/rails-single/public/406-unsupported-browser.html +114 -0
  117. data/examples/rails-single/public/422.html +114 -0
  118. data/examples/rails-single/public/500.html +114 -0
  119. data/examples/rails-single/public/icon.png +0 -0
  120. data/examples/rails-single/public/icon.svg +3 -0
  121. data/examples/rails-single/public/robots.txt +1 -0
  122. data/examples/rails-single/script/.keep +0 -0
  123. data/examples/rails-single/vendor/.keep +0 -0
  124. data/examples/rails-single/yarn.lock +188 -0
  125. data/exe/nvoi +6 -0
  126. data/lib/nvoi/cli.rb +190 -0
  127. data/lib/nvoi/cloudflare/client.rb +287 -0
  128. data/lib/nvoi/config/config.rb +248 -0
  129. data/lib/nvoi/config/env_resolver.rb +63 -0
  130. data/lib/nvoi/config/loader.rb +102 -0
  131. data/lib/nvoi/config/naming.rb +196 -0
  132. data/lib/nvoi/config/ssh_keys.rb +82 -0
  133. data/lib/nvoi/config/types.rb +274 -0
  134. data/lib/nvoi/constants.rb +59 -0
  135. data/lib/nvoi/credentials/crypto.rb +88 -0
  136. data/lib/nvoi/credentials/editor.rb +272 -0
  137. data/lib/nvoi/credentials/manager.rb +173 -0
  138. data/lib/nvoi/deployer/cleaner.rb +36 -0
  139. data/lib/nvoi/deployer/image_builder.rb +23 -0
  140. data/lib/nvoi/deployer/infrastructure.rb +126 -0
  141. data/lib/nvoi/deployer/orchestrator.rb +146 -0
  142. data/lib/nvoi/deployer/retry.rb +67 -0
  143. data/lib/nvoi/deployer/service_deployer.rb +311 -0
  144. data/lib/nvoi/deployer/tunnel_manager.rb +57 -0
  145. data/lib/nvoi/deployer/types.rb +8 -0
  146. data/lib/nvoi/errors.rb +67 -0
  147. data/lib/nvoi/k8s/renderer.rb +44 -0
  148. data/lib/nvoi/k8s/templates.rb +29 -0
  149. data/lib/nvoi/logger.rb +72 -0
  150. data/lib/nvoi/providers/aws.rb +403 -0
  151. data/lib/nvoi/providers/base.rb +111 -0
  152. data/lib/nvoi/providers/hetzner.rb +288 -0
  153. data/lib/nvoi/providers/hetzner_client.rb +170 -0
  154. data/lib/nvoi/remote/docker_manager.rb +203 -0
  155. data/lib/nvoi/remote/ssh_executor.rb +72 -0
  156. data/lib/nvoi/remote/volume_manager.rb +103 -0
  157. data/lib/nvoi/service/delete.rb +234 -0
  158. data/lib/nvoi/service/deploy.rb +80 -0
  159. data/lib/nvoi/service/exec.rb +144 -0
  160. data/lib/nvoi/service/provider.rb +36 -0
  161. data/lib/nvoi/steps/application_deployer.rb +26 -0
  162. data/lib/nvoi/steps/database_provisioner.rb +60 -0
  163. data/lib/nvoi/steps/k3s_cluster_setup.rb +105 -0
  164. data/lib/nvoi/steps/k3s_provisioner.rb +351 -0
  165. data/lib/nvoi/steps/server_provisioner.rb +43 -0
  166. data/lib/nvoi/steps/services_provisioner.rb +29 -0
  167. data/lib/nvoi/steps/tunnel_configurator.rb +66 -0
  168. data/lib/nvoi/steps/volume_provisioner.rb +154 -0
  169. data/lib/nvoi/version.rb +5 -0
  170. data/lib/nvoi.rb +79 -0
  171. data/templates/app-deployment.yaml.erb +102 -0
  172. data/templates/app-ingress.yaml.erb +20 -0
  173. data/templates/app-secret.yaml.erb +10 -0
  174. data/templates/app-service.yaml.erb +12 -0
  175. data/templates/db-statefulset.yaml.erb +76 -0
  176. data/templates/service-deployment.yaml.erb +91 -0
  177. data/templates/worker-deployment.yaml.erb +50 -0
  178. metadata +361 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: fb54d0e596f64c761d8c3b6f38e6ae0ea7f71186e89744078e6b09e9906bfc1a
4
+ data.tar.gz: 22eca4a7d6bc75e7d5f9c0372a261d3c82b88b07f3d82db38828c486249495c7
5
+ SHA512:
6
+ metadata.gz: 2f18b886aa28994c1f6f8caace570f1ad714815f4161ce2bbb48b00be06f7f3f60e7fa14e04d8c1423aa0b55039ca43165bf360c73fc11e8ccb73ea7a7b0a67f
7
+ data.tar.gz: 53d83331ca4ecd654fb1d563149f04ffae292474b2cf930cbeefafa00f6bbb746ade07fc1c400409445282718bb50eaf5db26dd0df187ac011313a242b3ca6a0
data/.rubocop.yml ADDED
@@ -0,0 +1,19 @@
1
+ # Omakase Ruby styling for Rails
2
+ inherit_gem: { rubocop-rails-omakase: rubocop.yml }
3
+
4
+ # Overwrite or add rules to create your own house style
5
+ #
6
+ # # Use `[a, [b, c]]` not `[ a, [ b, c ] ]`
7
+ Layout/SpaceInsideArrayLiteralBrackets:
8
+ Enabled: false
9
+
10
+ # Enforce Ruby 3.1+ hash shorthand syntax
11
+ Style/HashSyntax:
12
+ EnforcedStyle: ruby19_no_mixed_keys
13
+ EnforcedShorthandSyntax: always
14
+
15
+ Layout/IndentationConsistency:
16
+ Enabled: true
17
+
18
+ Layout/IndentationWidth:
19
+ Enabled: true
data/Gemfile ADDED
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gemspec
6
+
7
+ group :development, :test do
8
+ gem "rubocop-rails-omakase", require: false
9
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,151 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ nvoi (0.1.5)
5
+ aws-sdk-ec2 (~> 1.400)
6
+ faraday (~> 2.7)
7
+ net-scp (~> 4.0)
8
+ net-ssh (~> 7.2)
9
+ thor (~> 1.3)
10
+
11
+ GEM
12
+ remote: https://rubygems.org/
13
+ specs:
14
+ activesupport (8.1.1)
15
+ base64
16
+ bigdecimal
17
+ concurrent-ruby (~> 1.0, >= 1.3.1)
18
+ connection_pool (>= 2.2.5)
19
+ drb
20
+ i18n (>= 1.6, < 2)
21
+ json
22
+ logger (>= 1.4.2)
23
+ minitest (>= 5.1)
24
+ securerandom (>= 0.3)
25
+ tzinfo (~> 2.0, >= 2.0.5)
26
+ uri (>= 0.13.1)
27
+ addressable (2.8.8)
28
+ public_suffix (>= 2.0.2, < 8.0)
29
+ ast (2.4.3)
30
+ aws-eventstream (1.4.0)
31
+ aws-partitions (1.1191.0)
32
+ aws-sdk-core (3.239.2)
33
+ aws-eventstream (~> 1, >= 1.3.0)
34
+ aws-partitions (~> 1, >= 1.992.0)
35
+ aws-sigv4 (~> 1.9)
36
+ base64
37
+ bigdecimal
38
+ jmespath (~> 1, >= 1.6.1)
39
+ logger
40
+ aws-sdk-ec2 (1.584.0)
41
+ aws-sdk-core (~> 3, >= 3.239.1)
42
+ aws-sigv4 (~> 1.5)
43
+ aws-sigv4 (1.12.1)
44
+ aws-eventstream (~> 1, >= 1.0.2)
45
+ base64 (0.3.0)
46
+ bigdecimal (3.3.1)
47
+ concurrent-ruby (1.3.5)
48
+ connection_pool (3.0.2)
49
+ crack (1.0.1)
50
+ bigdecimal
51
+ rexml
52
+ drb (2.2.3)
53
+ faraday (2.14.0)
54
+ faraday-net_http (>= 2.0, < 3.5)
55
+ json
56
+ logger
57
+ faraday-net_http (3.4.2)
58
+ net-http (~> 0.5)
59
+ hashdiff (1.2.1)
60
+ i18n (1.14.7)
61
+ concurrent-ruby (~> 1.0)
62
+ jmespath (1.6.2)
63
+ json (2.17.1)
64
+ language_server-protocol (3.17.0.5)
65
+ lint_roller (1.1.0)
66
+ logger (1.7.0)
67
+ minitest (5.26.2)
68
+ net-http (0.8.0)
69
+ uri (>= 0.11.1)
70
+ net-scp (4.1.0)
71
+ net-ssh (>= 2.6.5, < 8.0.0)
72
+ net-ssh (7.3.0)
73
+ parallel (1.27.0)
74
+ parser (3.3.10.0)
75
+ ast (~> 2.4.1)
76
+ racc
77
+ prism (1.6.0)
78
+ public_suffix (7.0.0)
79
+ racc (1.8.1)
80
+ rack (3.2.4)
81
+ rainbow (3.1.1)
82
+ rake (13.3.1)
83
+ regexp_parser (2.11.3)
84
+ rexml (3.4.4)
85
+ rubocop (1.81.7)
86
+ json (~> 2.3)
87
+ language_server-protocol (~> 3.17.0.2)
88
+ lint_roller (~> 1.1.0)
89
+ parallel (~> 1.10)
90
+ parser (>= 3.3.0.2)
91
+ rainbow (>= 2.2.2, < 4.0)
92
+ regexp_parser (>= 2.9.3, < 3.0)
93
+ rubocop-ast (>= 1.47.1, < 2.0)
94
+ ruby-progressbar (~> 1.7)
95
+ unicode-display_width (>= 2.4.0, < 4.0)
96
+ rubocop-ast (1.48.0)
97
+ parser (>= 3.3.7.2)
98
+ prism (~> 1.4)
99
+ rubocop-performance (1.26.1)
100
+ lint_roller (~> 1.1)
101
+ rubocop (>= 1.75.0, < 2.0)
102
+ rubocop-ast (>= 1.47.1, < 2.0)
103
+ rubocop-rails (2.34.2)
104
+ activesupport (>= 4.2.0)
105
+ lint_roller (~> 1.1)
106
+ rack (>= 1.1)
107
+ rubocop (>= 1.75.0, < 2.0)
108
+ rubocop-ast (>= 1.44.0, < 2.0)
109
+ rubocop-rails-omakase (1.1.0)
110
+ rubocop (>= 1.72)
111
+ rubocop-performance (>= 1.24)
112
+ rubocop-rails (>= 2.30)
113
+ ruby-progressbar (1.13.0)
114
+ securerandom (0.4.1)
115
+ thor (1.4.0)
116
+ tzinfo (2.0.6)
117
+ concurrent-ruby (~> 1.0)
118
+ unicode-display_width (3.2.0)
119
+ unicode-emoji (~> 4.1)
120
+ unicode-emoji (4.1.0)
121
+ uri (1.1.1)
122
+ webmock (3.26.1)
123
+ addressable (>= 2.8.0)
124
+ crack (>= 0.3.2)
125
+ hashdiff (>= 0.4.0, < 2.0.0)
126
+
127
+ PLATFORMS
128
+ aarch64-linux-gnu
129
+ aarch64-linux-musl
130
+ arm-linux-gnu
131
+ arm-linux-musl
132
+ arm64-darwin
133
+ ruby
134
+ x86-linux-gnu
135
+ x86-linux-musl
136
+ x86_64-darwin
137
+ x86_64-linux
138
+ x86_64-linux-gnu
139
+ x86_64-linux-musl
140
+
141
+ DEPENDENCIES
142
+ bundler (~> 2.0)
143
+ minitest (~> 5.20)
144
+ nvoi!
145
+ rake (~> 13.0)
146
+ rubocop (~> 1.57)
147
+ rubocop-rails-omakase
148
+ webmock (~> 3.19)
149
+
150
+ BUNDLED WITH
151
+ 2.6.5
data/Makefile ADDED
@@ -0,0 +1,26 @@
1
+ NVOI = ruby -I$(PWD)/lib $(PWD)/exe/nvoi
2
+ EXAMPLES = $(PWD)/examples
3
+
4
+ deploy-golang:
5
+ cd $(EXAMPLES)/golang && $(NVOI) deploy
6
+
7
+ exec-golang:
8
+ cd $(EXAMPLES)/golang && $(NVOI) exec -i
9
+
10
+ show-golang:
11
+ cd $(EXAMPLES)/golang && $(NVOI) credentials show
12
+
13
+ delete-golang:
14
+ cd $(EXAMPLES)/golang && $(NVOI) delete
15
+
16
+ deploy-rails:
17
+ cd $(EXAMPLES)/rails-single && $(NVOI) deploy
18
+
19
+ delete-rails:
20
+ cd $(EXAMPLES)/rails-single && $(NVOI) delete
21
+
22
+ test:
23
+ bundle exec rake test
24
+
25
+ lint:
26
+ bundle exec rubocop
data/Rakefile ADDED
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rake/testtask"
5
+ require "rubocop/rake_task"
6
+
7
+ Rake::TestTask.new(:test) do |t|
8
+ t.libs << "test"
9
+ t.libs << "lib"
10
+ t.test_files = FileList["test/**/*_test.rb"]
11
+ t.warning = false
12
+ end
13
+
14
+ RuboCop::RakeTask.new
15
+
16
+ task default: %i[test]
@@ -0,0 +1,357 @@
1
+ openapi: 3.1.0
2
+ info:
3
+ title: NVOI Configuration Schema
4
+ version: 0.1.2
5
+ description: |
6
+ OpenAPI 3.1 / JSON Schema definition for NVOI deployment configuration.
7
+ This schema defines the structure of deploy.yml / deploy.enc files.
8
+
9
+ components:
10
+ schemas:
11
+ DeployConfig:
12
+ type: object
13
+ description: Root deployment configuration
14
+ required:
15
+ - application
16
+ properties:
17
+ application:
18
+ $ref: "#/components/schemas/Application"
19
+
20
+ Application:
21
+ type: object
22
+ description: Application-level configuration
23
+ required:
24
+ - name
25
+ properties:
26
+ name:
27
+ type: string
28
+ description: Application name (used for resource naming)
29
+ example: myapp
30
+ environment:
31
+ type: string
32
+ description: Deployment environment
33
+ default: production
34
+ example: production
35
+ domain_provider:
36
+ $ref: "#/components/schemas/DomainProviderConfig"
37
+ compute_provider:
38
+ $ref: "#/components/schemas/ComputeProviderConfig"
39
+ keep_count:
40
+ type: integer
41
+ description: Number of old deployments to keep for rollback
42
+ minimum: 0
43
+ example: 3
44
+ servers:
45
+ type: object
46
+ description: Server group definitions (key is group name)
47
+ additionalProperties:
48
+ $ref: "#/components/schemas/ServerConfig"
49
+ example:
50
+ master:
51
+ type: cx32
52
+ location: fsn1
53
+ workers:
54
+ count: 2
55
+ type: cx22
56
+ app:
57
+ type: object
58
+ description: Application services (key is service name)
59
+ additionalProperties:
60
+ $ref: "#/components/schemas/AppServiceConfig"
61
+ example:
62
+ web:
63
+ port: 3000
64
+ domain: example.com
65
+ database:
66
+ $ref: "#/components/schemas/DatabaseConfig"
67
+ services:
68
+ type: object
69
+ description: Additional services like Redis, etc. (key is service name)
70
+ additionalProperties:
71
+ $ref: "#/components/schemas/ServiceConfig"
72
+ example:
73
+ redis:
74
+ image: redis:7-alpine
75
+ env:
76
+ type: object
77
+ description: Global environment variables
78
+ additionalProperties:
79
+ type: string
80
+ example:
81
+ RAILS_ENV: production
82
+ LOG_LEVEL: info
83
+ secrets:
84
+ type: object
85
+ description: Secret environment variables (stored encrypted)
86
+ additionalProperties:
87
+ type: string
88
+ example:
89
+ DATABASE_URL: postgres://user:pass@host/db
90
+ ssh_keys:
91
+ $ref: "#/components/schemas/SSHKeyConfig"
92
+
93
+ DomainProviderConfig:
94
+ type: object
95
+ description: Domain/DNS provider configuration
96
+ properties:
97
+ cloudflare:
98
+ $ref: "#/components/schemas/CloudflareConfig"
99
+
100
+ ComputeProviderConfig:
101
+ type: object
102
+ description: Compute provider configuration (one of hetzner or aws)
103
+ properties:
104
+ hetzner:
105
+ $ref: "#/components/schemas/HetznerConfig"
106
+ aws:
107
+ $ref: "#/components/schemas/AWSConfig"
108
+
109
+ CloudflareConfig:
110
+ type: object
111
+ description: Cloudflare provider configuration
112
+ required:
113
+ - api_token
114
+ - account_id
115
+ properties:
116
+ api_token:
117
+ type: string
118
+ description: Cloudflare API token with tunnel and DNS permissions
119
+ example: xxxxx
120
+ account_id:
121
+ type: string
122
+ description: Cloudflare account ID
123
+ example: xxxxx
124
+
125
+ HetznerConfig:
126
+ type: object
127
+ description: Hetzner Cloud provider configuration
128
+ required:
129
+ - api_token
130
+ properties:
131
+ api_token:
132
+ type: string
133
+ description: Hetzner Cloud API token
134
+ example: xxxxx
135
+ server_type:
136
+ type: string
137
+ description: Default server type for all server groups
138
+ example: cx22
139
+ server_location:
140
+ type: string
141
+ description: Default datacenter location
142
+ enum: [fsn1, nbg1, hel1, ash, hil]
143
+ example: fsn1
144
+
145
+ AWSConfig:
146
+ type: object
147
+ description: AWS provider configuration
148
+ required:
149
+ - access_key_id
150
+ - secret_access_key
151
+ - region
152
+ properties:
153
+ access_key_id:
154
+ type: string
155
+ description: AWS access key ID
156
+ secret_access_key:
157
+ type: string
158
+ description: AWS secret access key
159
+ region:
160
+ type: string
161
+ description: AWS region
162
+ example: us-east-1
163
+ instance_type:
164
+ type: string
165
+ description: Default EC2 instance type
166
+ example: t3.medium
167
+
168
+ ServerConfig:
169
+ type: object
170
+ description: Server group configuration
171
+ properties:
172
+ master:
173
+ type: boolean
174
+ description: Whether this group contains the K3s master node
175
+ default: false
176
+ type:
177
+ type: string
178
+ description: Server type (overrides compute_provider default)
179
+ example: cx32
180
+ location:
181
+ type: string
182
+ description: Datacenter location (overrides compute_provider default)
183
+ example: fsn1
184
+ count:
185
+ type: integer
186
+ description: Number of servers in this group
187
+ minimum: 1
188
+ default: 1
189
+ example: 2
190
+
191
+ AppServiceConfig:
192
+ type: object
193
+ description: Application service configuration (web, worker, etc.)
194
+ properties:
195
+ servers:
196
+ type: array
197
+ description: Server groups this service runs on
198
+ items:
199
+ type: string
200
+ example: [master]
201
+ domain:
202
+ type: string
203
+ description: Domain for this service (must be on Cloudflare)
204
+ example: example.com
205
+ subdomain:
206
+ type: string
207
+ description: Subdomain for this service (use @ for apex)
208
+ example: www
209
+ port:
210
+ type: integer
211
+ description: Container port to expose
212
+ minimum: 1
213
+ maximum: 65535
214
+ example: 3000
215
+ healthcheck:
216
+ $ref: "#/components/schemas/HealthCheckConfig"
217
+ command:
218
+ type: string
219
+ description: Override container command
220
+ example: bundle exec puma -C config/puma.rb
221
+ pre_run_command:
222
+ type: string
223
+ description: Command to run before deployment (e.g., migrations)
224
+ example: bundle exec rails db:migrate
225
+ env:
226
+ type: object
227
+ description: Service-specific environment variables
228
+ additionalProperties:
229
+ type: string
230
+ volumes:
231
+ type: object
232
+ description: Volume mounts (key is volume name, value is mount path)
233
+ additionalProperties:
234
+ type: string
235
+ example:
236
+ data: /app/data
237
+ uploads: /app/public/uploads
238
+
239
+ HealthCheckConfig:
240
+ type: object
241
+ description: Health check configuration for readiness/liveness probes
242
+ properties:
243
+ type:
244
+ type: string
245
+ description: Health check type
246
+ enum: [http, tcp, exec]
247
+ example: http
248
+ path:
249
+ type: string
250
+ description: HTTP path for health check (type=http)
251
+ example: /health
252
+ port:
253
+ type: integer
254
+ description: Port for health check (defaults to service port)
255
+ example: 3000
256
+ command:
257
+ type: string
258
+ description: Command for exec health check (type=exec)
259
+ example: /bin/healthcheck
260
+ interval:
261
+ type: string
262
+ description: Time between health checks
263
+ example: 10s
264
+ timeout:
265
+ type: string
266
+ description: Health check timeout
267
+ example: 5s
268
+ retries:
269
+ type: integer
270
+ description: Number of retries before marking unhealthy
271
+ minimum: 1
272
+ example: 3
273
+
274
+ DatabaseConfig:
275
+ type: object
276
+ description: Database configuration
277
+ properties:
278
+ servers:
279
+ type: array
280
+ description: Server groups to run database on
281
+ items:
282
+ type: string
283
+ example: [master]
284
+ adapter:
285
+ type: string
286
+ description: Database adapter type
287
+ enum: [postgresql, postgres, mysql, sqlite3]
288
+ example: postgres
289
+ url:
290
+ type: string
291
+ description: Database connection URL (for external databases)
292
+ example: postgres://user:pass@host:5432/dbname
293
+ image:
294
+ type: string
295
+ description: Docker image for database (managed databases only)
296
+ example: postgres:15-alpine
297
+ volume:
298
+ type: string
299
+ description: Volume mount path for data persistence
300
+ example: /var/lib/postgresql/data
301
+ secrets:
302
+ type: object
303
+ description: Database secrets (POSTGRES_PASSWORD, etc.)
304
+ additionalProperties:
305
+ type: string
306
+ example:
307
+ POSTGRES_USER: app
308
+ POSTGRES_PASSWORD: secretpassword
309
+ POSTGRES_DB: app_production
310
+
311
+ ServiceConfig:
312
+ type: object
313
+ description: Additional service configuration (Redis, etc.)
314
+ properties:
315
+ servers:
316
+ type: array
317
+ description: Server groups to run service on
318
+ items:
319
+ type: string
320
+ example: [master]
321
+ image:
322
+ type: string
323
+ description: Docker image for service
324
+ example: redis:7-alpine
325
+ command:
326
+ type: string
327
+ description: Override container command
328
+ example: redis-server --appendonly yes
329
+ env:
330
+ type: object
331
+ description: Service environment variables
332
+ additionalProperties:
333
+ type: string
334
+ volume:
335
+ type: string
336
+ description: Volume mount path for data persistence
337
+ example: /data
338
+
339
+ SSHKeyConfig:
340
+ type: object
341
+ description: SSH key content for server access (auto-generated on first run)
342
+ required:
343
+ - private_key
344
+ - public_key
345
+ properties:
346
+ private_key:
347
+ type: string
348
+ description: Private SSH key content (Ed25519 format, auto-generated)
349
+ example: |
350
+ -----BEGIN OPENSSH PRIVATE KEY-----
351
+ b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
352
+ ...
353
+ -----END OPENSSH PRIVATE KEY-----
354
+ public_key:
355
+ type: string
356
+ description: Public SSH key content (OpenSSH format, auto-generated)
357
+ example: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI... nvoi-deploy
@@ -0,0 +1,68 @@
1
+ # Example: Apex domain and wildcard subdomain support
2
+ #
3
+ # This example demonstrates three subdomain patterns:
4
+ # - "@" or "" : Apex domain (example.com)
5
+ # - "*" : Wildcard (*.example.com)
6
+ # - "app" : Standard subdomain (app.example.com)
7
+
8
+ application:
9
+ name: apex-wildcard-demo
10
+ environment: production
11
+
12
+ domain_provider:
13
+ cloudflare:
14
+ api_token: $CLOUDFLARE_API_TOKEN
15
+ account_id: $CLOUDFLARE_ACCOUNT_ID
16
+
17
+ compute_provider:
18
+ hetzner:
19
+ api_token: $HETZNER_API_TOKEN
20
+ server_type: cx22
21
+ server_location: fsn1
22
+
23
+ servers:
24
+ master:
25
+ type: cx22
26
+ location: fsn1
27
+
28
+ keep_count: 2
29
+
30
+ app:
31
+ # Apex domain: accessible at example.com (no subdomain)
32
+ # Use "@" or "" for apex/root domain
33
+ main:
34
+ servers: [master]
35
+ domain: example.com
36
+ subdomain: "@" # or use: subdomain: ""
37
+ port: 3000
38
+ healthcheck:
39
+ type: http
40
+ path: /health
41
+ port: 3000
42
+
43
+ # Wildcard subdomain: accessible at *.example.com
44
+ # Catches all subdomains not explicitly defined
45
+ wildcard:
46
+ servers: [master]
47
+ domain: example.com
48
+ subdomain: "*"
49
+ port: 3001
50
+ healthcheck:
51
+ type: http
52
+ path: /health
53
+ port: 3001
54
+
55
+ # Standard subdomain: accessible at api.example.com
56
+ api:
57
+ servers: [master]
58
+ domain: example.com
59
+ subdomain: api
60
+ port: 3002
61
+ healthcheck:
62
+ type: http
63
+ path: /health
64
+ port: 3002
65
+
66
+ env:
67
+ APP_NAME: apex-wildcard-demo
68
+ LOG_LEVEL: info
@@ -0,0 +1,19 @@
1
+ # Environment files
2
+ .env
3
+ .env.*
4
+ !.env.example
5
+
6
+ # Database files
7
+ data/
8
+ *.db
9
+ *.sqlite3
10
+
11
+ # Build artifacts
12
+ app
13
+ *.exe
14
+
15
+ # Go workspace
16
+ go.work
17
+
18
+ # NVOI master key (do not commit)
19
+ deploy.key