@ebowwa/hetzner 0.2.2 → 0.3.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.
Files changed (61) hide show
  1. package/dist/bootstrap/index.js +1126 -0
  2. package/dist/bootstrap/index.js.map +15 -0
  3. package/dist/index.js +3540 -0
  4. package/dist/index.js.map +31 -0
  5. package/dist/onboarding/index.js +460 -0
  6. package/dist/onboarding/index.js.map +14 -0
  7. package/package.json +53 -16
  8. package/actions.js +0 -1084
  9. package/actions.ts +0 -1053
  10. package/auth.js +0 -39
  11. package/auth.ts +0 -37
  12. package/bootstrap/FIREWALL.md +0 -326
  13. package/bootstrap/KERNEL-HARDENING.md +0 -258
  14. package/bootstrap/SECURITY-INTEGRATION.md +0 -281
  15. package/bootstrap/TESTING.md +0 -301
  16. package/bootstrap/cloud-init.js +0 -323
  17. package/bootstrap/cloud-init.ts +0 -394
  18. package/bootstrap/firewall.js +0 -292
  19. package/bootstrap/firewall.ts +0 -342
  20. package/bootstrap/genesis.js +0 -424
  21. package/bootstrap/genesis.ts +0 -518
  22. package/bootstrap/index.js +0 -59
  23. package/bootstrap/index.ts +0 -71
  24. package/bootstrap/kernel-hardening.js +0 -270
  25. package/bootstrap/kernel-hardening.test.js +0 -182
  26. package/bootstrap/kernel-hardening.test.ts +0 -230
  27. package/bootstrap/kernel-hardening.ts +0 -272
  28. package/bootstrap/security-audit.js +0 -122
  29. package/bootstrap/security-audit.ts +0 -124
  30. package/bootstrap/ssh-hardening.js +0 -186
  31. package/bootstrap/ssh-hardening.ts +0 -192
  32. package/client.js +0 -234
  33. package/client.ts +0 -177
  34. package/config.js +0 -7
  35. package/config.ts +0 -5
  36. package/errors.js +0 -345
  37. package/errors.ts +0 -371
  38. package/index.js +0 -73
  39. package/index.ts +0 -59
  40. package/onboarding/doppler.ts +0 -116
  41. package/onboarding/git.ts +0 -133
  42. package/onboarding/index.ts +0 -18
  43. package/onboarding/onboarding.ts +0 -193
  44. package/onboarding/tailscale.ts +0 -159
  45. package/onboarding/types.ts +0 -115
  46. package/pricing.js +0 -387
  47. package/pricing.ts +0 -422
  48. package/schemas.js +0 -667
  49. package/schemas.ts +0 -765
  50. package/server-status.js +0 -122
  51. package/server-status.ts +0 -81
  52. package/servers.js +0 -667
  53. package/servers.ts +0 -568
  54. package/ssh-keys.js +0 -180
  55. package/ssh-keys.ts +0 -122
  56. package/ssh-setup.js +0 -253
  57. package/ssh-setup.ts +0 -218
  58. package/types.js +0 -99
  59. package/types.ts +0 -389
  60. package/volumes.js +0 -295
  61. package/volumes.ts +0 -229
package/onboarding/git.ts DELETED
@@ -1,133 +0,0 @@
1
- /**
2
- * Git Onboarding
3
- *
4
- * Configure Git credentials on remote servers.
5
- * Sets up GitHub token for gh cli and git credential helper.
6
- */
7
-
8
- import type { SSHOptions } from "@ebowwa/terminal/types";
9
-
10
- export interface GitConfig {
11
- username?: string;
12
- email?: string;
13
- githubToken?: string;
14
- }
15
-
16
- /**
17
- * Configure Git on a remote server
18
- *
19
- * @param host - Server IP or hostname
20
- * @param config - Git configuration
21
- * @param sshOptions - SSH options
22
- * @returns Success status
23
- */
24
- export async function onboardGit(
25
- host: string,
26
- config: GitConfig,
27
- sshOptions: Partial<SSHOptions> = {}
28
- ): Promise<{ success: boolean; message: string }> {
29
- const { execSSH } = await import("@ebowwa/terminal/client");
30
-
31
- const { username = "root", email, githubToken } = config;
32
-
33
- try {
34
- const commands: string[] = [];
35
-
36
- // Configure git user
37
- commands.push(`git config --global user.name "${username}"`);
38
-
39
- if (email) {
40
- commands.push(`git config --global user.email "${email}"`);
41
- }
42
-
43
- // Configure credential helper
44
- commands.push(`
45
- git config --global credential.helper store
46
- mkdir -p ~/.config/gh
47
- `);
48
-
49
- // Configure GitHub CLI token if provided
50
- if (githubToken) {
51
- commands.push(`
52
- echo "github.com:" ${username}:${githubToken} > ~/.git-credentials
53
- chmod 600 ~/.git-credentials
54
- echo "GITHUB_TOKEN=${githubToken}" > ~/.config/gh/hosts.yml
55
- chmod 600 ~/.config/gh/hosts.yml
56
- `);
57
- }
58
-
59
- const cmd = commands.join(" && ");
60
-
61
- await execSSH(cmd, {
62
- host,
63
- user: "root",
64
- timeout: 10000,
65
- ...sshOptions
66
- });
67
-
68
- return {
69
- success: true,
70
- message: `Git configured for ${username}${email ? ` (${email})` : ""}`
71
- };
72
- } catch (error) {
73
- return {
74
- success: false,
75
- message: `Git configuration failed: ${error instanceof Error ? error.message : String(error)}`
76
- };
77
- }
78
- }
79
-
80
- /**
81
- * Check Git status on a remote server
82
- *
83
- * @param host - Server IP or hostname
84
- * @param sshOptions - SSH options
85
- * @returns Git status
86
- */
87
- export async function checkGitStatus(
88
- host: string,
89
- sshOptions: Partial<SSHOptions> = {}
90
- ): Promise<{ configured: boolean; username?: string; email?: string; hasToken: boolean; message: string }> {
91
- const { execSSH } = await import("@ebowwa/terminal/client");
92
-
93
- try {
94
- const checkCmd = `
95
- git config --global user.name 2>/dev/null || echo "NOT_SET"
96
- git config --global user.email 2>/dev/null || echo "NOT_SET"
97
- test -f ~/.git-credentials && echo "HAS_CREDS" || echo "NO_CREDS"
98
- test -f ~/.config/gh/hosts.yml && echo "HAS_GH_TOKEN" || echo "NO_GH_TOKEN"
99
- `;
100
-
101
- const result = await execSSH(checkCmd, {
102
- host,
103
- user: "root",
104
- timeout: 5000,
105
- ...sshOptions
106
- });
107
-
108
- const lines = result.trim().split("\n");
109
-
110
- const username = lines[0]?.trim();
111
- const email = lines[1]?.trim();
112
- const hasCreds = lines[2]?.includes("HAS_CREDS");
113
- const hasGhToken = lines[3]?.includes("HAS_GH_TOKEN");
114
-
115
- const configured = username !== "NOT_SET";
116
-
117
- return {
118
- configured,
119
- username: username !== "NOT_SET" ? username : undefined,
120
- email: email !== "NOT_SET" ? email : undefined,
121
- hasToken: hasCreds || hasGhToken,
122
- message: configured
123
- ? `Git configured: ${username}${email ? ` <${email}>` : ""}`
124
- : "Git not configured"
125
- };
126
- } catch (error) {
127
- return {
128
- configured: false,
129
- hasToken: false,
130
- message: `Could not check Git status: ${error instanceof Error ? error.message : String(error)}`
131
- };
132
- }
133
- }
@@ -1,18 +0,0 @@
1
- /**
2
- * Hetzner Onboarding Module
3
- *
4
- * Post-boot configuration for newly provisioned servers.
5
- * Handles services that require secrets/tokens:
6
- * - Doppler (secrets management)
7
- * - Tailscale (VPN)
8
- * - Git (credentials)
9
- *
10
- * This is Phase 2 of the bootstrap process, running after
11
- * cloud-init completes the initial installation.
12
- */
13
-
14
- export { OnboardingConfig, OnboardingStatus, OnboardingResult } from "./types.js";
15
- export { onboardDoppler, checkDopplerStatus } from "./doppler.js";
16
- export { onboardTailscale, checkTailscaleStatus } from "./tailscale.js";
17
- export { onboardGit, checkGitStatus } from "./git.js";
18
- export { onboardServer, checkOnboardingStatus, onboardBatch } from "./onboarding.js";
@@ -1,193 +0,0 @@
1
- /**
2
- * Server Onboarding
3
- *
4
- * Main onboarding orchestration.
5
- * Coordinates Doppler, Tailscale, and Git onboarding.
6
- */
7
-
8
- import type { SSHOptions } from "@ebowwa/terminal/types";
9
- import type {
10
- OnboardingConfig,
11
- OnboardingStatus,
12
- OnboardingResult,
13
- BatchOnboardingResult,
14
- } from "./types.js";
15
- import { checkDopplerStatus, onboardDoppler } from "./doppler.js";
16
- import { checkTailscaleStatus, onboardTailscale } from "./tailscale.js";
17
- import { checkGitStatus, onboardGit } from "./git.js";
18
-
19
- /**
20
- * Onboard a single server with all configured services
21
- *
22
- * @param config - Onboarding configuration
23
- * @param sshOptions - Additional SSH options
24
- * @returns Onboarding result
25
- */
26
- export async function onboardServer(
27
- config: OnboardingConfig,
28
- sshOptions: Partial<SSHOptions> = {}
29
- ): Promise<OnboardingResult> {
30
- const { host, user = "root", doppler, tailscale, git } = config;
31
-
32
- const configured: ("doppler" | "tailscale" | "git")[] = [];
33
- const failed: Array<{
34
- service: "doppler" | "tailscale" | "git";
35
- error: string;
36
- }> = [];
37
-
38
- const baseSshOptions: Partial<SSHOptions> = {
39
- user,
40
- ...sshOptions
41
- };
42
-
43
- // Onboard Doppler
44
- if (doppler) {
45
- const result = await onboardDoppler(host, doppler, baseSshOptions);
46
- if (result.success) {
47
- configured.push("doppler");
48
- } else {
49
- failed.push({ service: "doppler", error: result.message });
50
- }
51
- }
52
-
53
- // Onboard Tailscale
54
- if (tailscale) {
55
- const result = await onboardTailscale(host, tailscale, baseSshOptions);
56
- if (result.success) {
57
- configured.push("tailscale");
58
- } else {
59
- failed.push({ service: "tailscale", error: result.message });
60
- }
61
- }
62
-
63
- // Onboard Git
64
- if (git) {
65
- const result = await onboardGit(host, git, baseSshOptions);
66
- if (result.success) {
67
- configured.push("git");
68
- } else {
69
- failed.push({ service: "git", error: result.message });
70
- }
71
- }
72
-
73
- return {
74
- host,
75
- configured,
76
- failed,
77
- success: failed.length === 0,
78
- completedAt: new Date().toISOString(),
79
- };
80
- }
81
-
82
- /**
83
- * Check onboarding status of a server
84
- *
85
- * @param host - Server IP or hostname
86
- * @param sshOptions - SSH options
87
- * @returns Onboarding status
88
- */
89
- export async function checkOnboardingStatus(
90
- host: string,
91
- sshOptions: Partial<SSHOptions> = {}
92
- ): Promise<OnboardingStatus> {
93
- const user = "root";
94
-
95
- const [dopplerStatus, tailscaleStatus, gitStatus] = await Promise.all([
96
- checkDopplerStatus(host, { user, ...sshOptions }),
97
- checkTailscaleStatus(host, { user, ...sshOptions }),
98
- checkGitStatus(host, { user, ...sshOptions }),
99
- ]);
100
-
101
- const services = {
102
- doppler: {
103
- service: "doppler",
104
- configured: dopplerStatus.configured,
105
- message: dopplerStatus.message,
106
- details: dopplerStatus.project
107
- ? { project: dopplerStatus.project, config: dopplerStatus.config }
108
- : undefined,
109
- } as const,
110
- tailscale: {
111
- service: "tailscale",
112
- configured: tailscaleStatus.configured,
113
- message: tailscaleStatus.message,
114
- details: tailscaleStatus.tailscaleIp
115
- ? { tailscaleIp: tailscaleStatus.tailscaleIp, hostname: tailscaleStatus.hostname }
116
- : undefined,
117
- } as const,
118
- git: {
119
- service: "git",
120
- configured: gitStatus.configured,
121
- message: gitStatus.message,
122
- details: gitStatus.username
123
- ? { username: gitStatus.username, email: gitStatus.email }
124
- : undefined,
125
- } as const,
126
- };
127
-
128
- const complete = services.doppler.configured && services.tailscale.configured && services.git.configured;
129
-
130
- return {
131
- host,
132
- services,
133
- complete,
134
- checkedAt: new Date().toISOString(),
135
- };
136
- }
137
-
138
- /**
139
- * Onboard multiple servers in parallel
140
- *
141
- * @param hosts - List of server IPs/hostnames
142
- * @param sharedConfig - Shared onboarding config for all servers
143
- * @param sshOptions - SSH options
144
- * @returns Batch onboarding result
145
- */
146
- export async function onboardBatch(
147
- hosts: string[],
148
- sharedConfig: Omit<OnboardingConfig, "host">,
149
- sshOptions: Partial<SSHOptions> = {}
150
- ): Promise<BatchOnboardingResult> {
151
- // Onboard all servers in parallel
152
- const results = await Promise.all(
153
- hosts.map((host) =>
154
- onboardServer({ ...sharedConfig, host }, sshOptions)
155
- )
156
- );
157
-
158
- // Calculate summary
159
- const succeeded = results.filter((r) => r.success).length;
160
- const failed = results.filter((r) => !r.success).length;
161
- const partial = results.filter((r) => !r.success && r.configured.length > 0).length;
162
-
163
- return {
164
- results,
165
- summary: {
166
- total: results.length,
167
- succeeded,
168
- failed,
169
- partial,
170
- },
171
- };
172
- }
173
-
174
- /**
175
- * Quick onboarding check for multiple servers
176
- *
177
- * @param hosts - List of server IPs/hostnames
178
- * @param sshOptions - SSH options
179
- * @returns Map of host to onboarding status
180
- */
181
- export async function checkBatchStatus(
182
- hosts: string[],
183
- sshOptions: Partial<SSHOptions> = {}
184
- ): Promise<Map<string, OnboardingStatus>> {
185
- const statuses = await Promise.all(
186
- hosts.map(async (host) => {
187
- const status = await checkOnboardingStatus(host, sshOptions);
188
- return { host, status };
189
- })
190
- );
191
-
192
- return new Map(statuses.map(({ host, status }) => [host, status]));
193
- }
@@ -1,159 +0,0 @@
1
- /**
2
- * Tailscale Onboarding
3
- *
4
- * Join Tailscale network on remote servers.
5
- * Enables secure VPN access to servers.
6
- */
7
-
8
- import type { SSHOptions } from "@ebowwa/terminal/types";
9
-
10
- export interface TailscaleConfig {
11
- authKey: string;
12
- hostname?: string;
13
- tags?: string[];
14
- }
15
-
16
- /**
17
- * Join Tailscale network on a remote server
18
- *
19
- * @param host - Server IP or hostname
20
- * @param config - Tailscale configuration
21
- * @param sshOptions - SSH options
22
- * @returns Success status with Tailscale IP
23
- */
24
- export async function onboardTailscale(
25
- host: string,
26
- config: TailscaleConfig,
27
- sshOptions: Partial<SSHOptions> = {}
28
- ): Promise<{ success: boolean; message: string; tailscaleIp?: string }> {
29
- const { execSSH } = await import("@ebowwa/terminal/client");
30
-
31
- const { authKey, hostname, tags = [] } = config;
32
-
33
- try {
34
- // Step 1: Install Tailscale if not present
35
- const installCmd = `
36
- if ! command -v tailscale &>/dev/null; then
37
- curl -fsSL https://tailscale.com/install.sh | sh
38
- fi
39
- `;
40
-
41
- await execSSH(installCmd, {
42
- host,
43
- user: "root",
44
- timeout: 60000, // 1 minute for install
45
- ...sshOptions
46
- });
47
-
48
- // Step 2: Build tailscale up command
49
- const upArgs = ["up", "--authkey", authKey];
50
-
51
- if (hostname) {
52
- upArgs.push("--hostname", hostname);
53
- }
54
-
55
- if (tags.length > 0) {
56
- upArgs.push("--tags", tags.join(","));
57
- }
58
-
59
- const upCmd = `tailscale ${upArgs.join(" ")}`;
60
-
61
- // Step 3: Run tailscale up
62
- await execSSH(upCmd, {
63
- host,
64
- user: "root",
65
- timeout: 30000,
66
- ...sshOptions
67
- });
68
-
69
- // Step 4: Get Tailscale IP
70
- const ipCmd = "tailscale ip -4 2>/dev/null || tailscale ip 2>/dev/null || echo 'PENDING'";
71
-
72
- const ipResult = await execSSH(ipCmd, {
73
- host,
74
- user: "root",
75
- timeout: 5000,
76
- ...sshOptions
77
- });
78
-
79
- const tailscaleIp = ipResult.trim();
80
-
81
- return {
82
- success: true,
83
- message: hostname
84
- ? `Tailscale joined as ${hostname}`
85
- : "Tailscale joined",
86
- tailscaleIp: tailscaleIp !== "PENDING" ? tailscaleIp : undefined
87
- };
88
- } catch (error) {
89
- return {
90
- success: false,
91
- message: `Tailscale onboarding failed: ${error instanceof Error ? error.message : String(error)}`
92
- };
93
- }
94
- }
95
-
96
- /**
97
- * Check Tailscale status on a remote server
98
- *
99
- * @param host - Server IP or hostname
100
- * @param sshOptions - SSH options
101
- * @returns Tailscale status
102
- */
103
- export async function checkTailscaleStatus(
104
- host: string,
105
- sshOptions: Partial<SSHOptions> = {}
106
- ): Promise<{ configured: boolean; tailscaleIp?: string; hostname?: string; message: string }> {
107
- const { execSSH } = await import("@ebowwa/terminal/client");
108
-
109
- try {
110
- // Check if tailscale is installed and running
111
- const checkCmd = `
112
- command -v tailscale &>/dev/null || echo "NOT_INSTALLED"
113
- tailscale status --json 2>/dev/null || echo "NOT_RUNNING"
114
- `;
115
-
116
- const result = await execSSH(checkCmd, {
117
- host,
118
- user: "root",
119
- timeout: 10000,
120
- ...sshOptions
121
- });
122
-
123
- const lines = result.trim().split("\n");
124
-
125
- if (lines[0]?.includes("NOT_INSTALLED")) {
126
- return {
127
- configured: false,
128
- message: "Tailscale not installed"
129
- };
130
- }
131
-
132
- const statusJson = lines[1] || "";
133
-
134
- if (statusJson.includes("NOT_RUNNING")) {
135
- return {
136
- configured: false,
137
- message: "Tailscale installed but not running"
138
- };
139
- }
140
-
141
- // Parse status JSON
142
- const status = JSON.parse(statusJson);
143
-
144
- const tailscaleIp = status.TailnetIPs?.[0] || status.IPv4;
145
- const hostname = status.HostName?.HostName || status.HostName;
146
-
147
- return {
148
- configured: true,
149
- tailscaleIp,
150
- hostname,
151
- message: `Tailscale connected as ${hostname || "unknown"} (${tailscaleIp || "no IP"})`
152
- };
153
- } catch (error) {
154
- return {
155
- configured: false,
156
- message: `Could not check Tailscale status: ${error instanceof Error ? error.message : String(error)}`
157
- };
158
- }
159
- }
@@ -1,115 +0,0 @@
1
- /**
2
- * Onboarding Types
3
- *
4
- * Type definitions for server onboarding operations.
5
- */
6
-
7
- /**
8
- * Onboarding configuration for a server
9
- */
10
- export interface OnboardingConfig {
11
- /** Server IP or hostname */
12
- host: string;
13
-
14
- /** SSH user (default: root) */
15
- user?: string;
16
-
17
- /** SSH port (default: 22) */
18
- port?: number;
19
-
20
- /** Doppler configuration */
21
- doppler?: {
22
- /** Doppler service token */
23
- token: string;
24
- /** Doppler project (default: seed) */
25
- project?: string;
26
- /** Doppler config (default: prd) */
27
- config?: string;
28
- };
29
-
30
- /** Tailscale configuration */
31
- tailscale?: {
32
- /** Tailscale auth key */
33
- authKey: string;
34
- /** Tailscale hostname (optional) */
35
- hostname?: string;
36
- /** Tailscale tags (optional) */
37
- tags?: string[];
38
- };
39
-
40
- /** Git configuration */
41
- git?: {
42
- /** Git username */
43
- username?: string;
44
- /** Git email */
45
- email?: string;
46
- /** GitHub token for gh cli */
47
- githubToken?: string;
48
- };
49
- }
50
-
51
- /**
52
- * Onboarding status for a single service
53
- */
54
- export interface ServiceStatus {
55
- /** Service name */
56
- service: "doppler" | "tailscale" | "git";
57
- /** Whether service is configured */
58
- configured: boolean;
59
- /** Status message */
60
- message: string;
61
- /** Configuration details (sanitized) */
62
- details?: Record<string, unknown>;
63
- }
64
-
65
- /**
66
- * Overall onboarding status
67
- */
68
- export interface OnboardingStatus {
69
- /** Server being checked */
70
- host: string;
71
- /** Status of each service */
72
- services: {
73
- doppler: ServiceStatus;
74
- tailscale: ServiceStatus;
75
- git: ServiceStatus;
76
- };
77
- /** Overall onboarding complete */
78
- complete: boolean;
79
- /** Timestamp */
80
- checkedAt: string;
81
- }
82
-
83
- /**
84
- * Result of an onboarding operation
85
- */
86
- export interface OnboardingResult {
87
- /** Server that was onboarded */
88
- host: string;
89
- /** Services that were configured */
90
- configured: ("doppler" | "tailscale" | "git")[];
91
- /** Services that failed */
92
- failed: Array<{
93
- service: "doppler" | "tailscale" | "git";
94
- error: string;
95
- }>;
96
- /** Overall success */
97
- success: boolean;
98
- /** Timestamp */
99
- completedAt: string;
100
- }
101
-
102
- /**
103
- * Batch onboarding result
104
- */
105
- export interface BatchOnboardingResult {
106
- /** Results for each server */
107
- results: OnboardingResult[];
108
- /** Summary statistics */
109
- summary: {
110
- total: number;
111
- succeeded: number;
112
- failed: number;
113
- partial: number;
114
- };
115
- }