Skip to content

Commit 0b8c4c1

Browse files
feat: switch to interactive wizard as default deployment mode
- Changed default deployment mode from YAML config to interactive wizard for better user experience - Updated help text to clarify wizard is now the recommended approach for human users - Added suggested modules list (bionicgpt, wazuh, nextcloud) in interactive prompts - Improved Authentik credential management and documentation: - Added detailed instructions for API token creation in .env template - Clarified difference between bootstrap
1 parent bf2844e commit 0b8c4c1

6 files changed

Lines changed: 156 additions & 78 deletions

File tree

cmd/create/hecate.go

Lines changed: 40 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ import (
1616
"github.com/spf13/cobra"
1717
"github.com/uptrace/opentelemetry-go-extra/otelzap"
1818
"go.uber.org/zap"
19-
"gopkg.in/yaml.v3"
2019
)
2120

2221
func init() {
@@ -36,8 +35,18 @@ var CreateHecateCmd = &cobra.Command{
3635
Short: "Deploy Hecate reverse proxy framework",
3736
Long: `Deploy Hecate reverse proxy framework using Docker Compose.
3837
39-
MODE 1: YAML Configuration (Recommended)
40-
Deploy from a simple YAML config file that defines your apps and their backends.
38+
MODE 1: Interactive Wizard (Default - Human-Centric)
39+
Run without any flags to start an interactive wizard that guides you through:
40+
- Adding apps and their domains
41+
- Configuring backends (upstream services)
42+
- Optional SSO integration via Authentik
43+
- WebRTC/TCP port forwarding setup
44+
45+
eos create hecate # Interactive wizard (recommended)
46+
47+
MODE 2: YAML Configuration (For Automation)
48+
Deploy from a YAML config file that defines your apps and backends.
49+
Useful for scripted deployments or complex multi-service setups.
4150
4251
Example config.yaml:
4352
apps:
@@ -52,22 +61,18 @@ MODE 1: YAML Configuration (Recommended)
5261
authentik:
5362
domain: hera.cybermonkey.dev
5463
55-
eos create hecate --config config.yaml
56-
57-
MODE 2: Interactive Wizard
58-
Interactive setup that guides you through configuration.
59-
60-
eos create hecate
64+
eos create hecate --config config.yaml # Deploy from YAML
6165
6266
The deployment creates configuration files at /opt/hecate/:
6367
- docker-compose.yml - Container orchestration
6468
- Caddyfile - Caddy reverse proxy configuration
65-
- .env - Environment variables (if Authentik is configured)
69+
- .env - Environment variables (includes Authentik bootstrap credentials)
6670
6771
Examples:
72+
eos create hecate # Interactive wizard (starts automatically)
6873
eos create hecate --config example.yaml # Deploy from YAML config
6974
eos create hecate --config example.yaml --output /opt/hecate # Custom output dir
70-
eos create hecate # Interactive wizard
75+
eos create config --hecate # Generate YAML config only (no deployment)
7176
eos create hecate --help # Show this help message`,
7277
RunE: eos.Wrap(func(rc *eos_io.RuntimeContext, cmd *cobra.Command, args []string) error {
7378
log := otelzap.Ctx(rc.Ctx)
@@ -95,6 +100,7 @@ Examples:
95100
}
96101

97102
var config *hecate.YAMLHecateConfig
103+
var tempConfigPath string
98104

99105
// Check if config file was provided (YAML override)
100106
if configFile != "" {
@@ -108,52 +114,36 @@ Examples:
108114
return fmt.Errorf("failed to load YAML config: %w", err)
109115
}
110116
} else {
111-
// No YAML file provided - try loading from Consul KV
112-
log.Info("No config file provided, checking Consul KV")
113-
114-
configStorage, err := hecate.NewConfigStorage(rc)
115-
if err != nil {
116-
return fmt.Errorf("failed to initialize Consul storage: %w\n\n"+
117-
"Either:\n"+
118-
" 1. Provide a YAML config file: eos create hecate --config hecate-config.yaml\n"+
119-
" 2. Generate config first: eos create config --hecate\n"+
120-
" 3. Ensure Consul is running and accessible", err)
121-
}
122-
123-
rawConfig, err := configStorage.LoadConfig(rc)
124-
if err != nil {
125-
return fmt.Errorf("failed to load config from Consul KV: %w", err)
126-
}
127-
128-
if rawConfig == nil || len(rawConfig.Apps) == 0 {
129-
return fmt.Errorf("no configuration found in Consul KV\n\n" +
130-
"Please run one of:\n" +
131-
" 1. Generate config: eos create config --hecate\n" +
132-
" 2. Provide YAML file: eos create hecate --config hecate-config.yaml")
133-
}
117+
// No YAML file provided - run interactive wizard
118+
log.Info("No config file provided, starting interactive wizard")
119+
120+
// Generate temporary config file path
121+
tempConfigPath = "/tmp/hecate-config-wizard.yaml"
122+
defer func() {
123+
if tempConfigPath != "" {
124+
_ = os.Remove(tempConfigPath)
125+
}
126+
}()
134127

135-
log.Info("Configuration loaded from Consul KV",
136-
zap.Int("app_count", len(rawConfig.Apps)))
128+
// Run interactive config generator (same as 'eos create config --hecate')
129+
log.Info("terminal prompt: ")
130+
log.Info("terminal prompt: No configuration file provided.")
131+
log.Info("terminal prompt: Starting interactive wizard to configure Hecate...")
132+
log.Info("terminal prompt: ")
137133

138-
// Convert RawYAMLConfig to parsed YAMLHecateConfig
139-
// We need to create a temporary YAML file to reuse LoadYAMLConfig
140-
// Or we can parse it directly here
141-
tempYAMLPath := "/tmp/hecate-config-from-consul.yaml"
142-
yamlData, err := yaml.Marshal(rawConfig)
143-
if err != nil {
144-
return fmt.Errorf("failed to marshal config: %w", err)
145-
}
146-
if err := os.WriteFile(tempYAMLPath, yamlData, 0644); err != nil {
147-
return fmt.Errorf("failed to write temp config: %w", err)
134+
if err := hecate.GenerateConfigFile(rc, tempConfigPath, true); err != nil {
135+
return fmt.Errorf("interactive configuration failed: %w", err)
148136
}
149-
defer func() { _ = os.Remove(tempYAMLPath) }()
150137

151-
config, err = hecate.LoadYAMLConfig(rc, tempYAMLPath)
138+
// Load the generated config
139+
config, err = hecate.LoadYAMLConfig(rc, tempConfigPath)
152140
if err != nil {
153-
return fmt.Errorf("failed to parse config from Consul: %w", err)
141+
return fmt.Errorf("failed to load generated config: %w", err)
154142
}
155143

156-
log.Info("terminal prompt: Using configuration from Consul KV")
144+
log.Info("terminal prompt: ")
145+
log.Info("terminal prompt: Configuration complete! Proceeding with deployment...")
146+
log.Info("terminal prompt: ")
157147
}
158148

159149
// Display detected apps

pkg/hecate/add/bionicgpt.go

Lines changed: 54 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -145,14 +145,18 @@ func (b *BionicGPTIntegrator) ConfigureAuthentication(rc *eos_io.RuntimeContext,
145145
}
146146

147147
// Step 1: Get Authentik credentials from .env file
148-
logger.Info(" Getting Authentik credentials from /opt/bionicgpt/.env")
148+
logger.Info(" Getting Authentik credentials from /opt/hecate/.env")
149149
authentikToken, authentikBaseURL, err := b.getAuthentikCredentials(rc.Ctx)
150150
if err != nil {
151151
logger.Warn("Authentik credentials not found, skipping proxy provider setup", zap.Error(err))
152-
logger.Warn("To enable authentication, add to /opt/bionicgpt/.env:")
153-
logger.Warn(" AUTHENTIK_TOKEN=your_authentik_api_token")
154-
logger.Warn(" AUTHENTIK_BASE_URL=http://localhost:9000")
155-
logger.Warn("Get API token from: https://hera.your-domain/if/admin/#/core/tokens")
152+
logger.Warn("")
153+
logger.Warn("To enable authentication:")
154+
logger.Warn(" 1. Login to Authentik admin: https://hera.codemonkey.net.au/if/admin/")
155+
logger.Warn(" 2. Create API token: Directory → Tokens → Create")
156+
logger.Warn(" 3. Add to /opt/hecate/.env:")
157+
logger.Warn(" echo 'AUTHENTIK_API_TOKEN=<your_token>' | sudo tee -a /opt/hecate/.env")
158+
logger.Warn(" 4. Re-run this command")
159+
logger.Warn("")
156160
return nil // Non-fatal: generic route still works
157161
}
158162

@@ -286,36 +290,63 @@ func (b *BionicGPTIntegrator) Rollback(rc *eos_io.RuntimeContext) error {
286290
}
287291

288292
// getAuthentikCredentials retrieves Authentik API credentials from .env files
289-
// TODO: Migrate to Vault once BionicGPT and Authentik are fully migrated to Vault-based secrets
293+
//
294+
// TODO (ROADMAP - 6-12 months): Migrate to Vault-based secret management
295+
// For now, credentials stored in /opt/hecate/.env (acceptable per CLAUDE.md requirements)
290296
func (b *BionicGPTIntegrator) getAuthentikCredentials(ctx context.Context) (string, string, error) {
291-
// TEMPORARY: Read from .env files until Vault migration is complete
292-
// BionicGPT stores Authentik token in /opt/bionicgpt/.env
293-
// Authentik base URL can be inferred from Hecate or use default
294-
295-
bionicgptEnv, err := readEnvFile("/opt/bionicgpt/.env")
297+
// ARCHITECTURE NOTE: Authentik runs in Hecate stack, credentials are in /opt/hecate/.env
298+
// NOT in /opt/bionicgpt/.env (BionicGPT is a separate service proxied through Authentik)
299+
//
300+
// TODO (UPSTREAM BLOCKER): Automate API token creation when Authentik provides capability
301+
// CURRENT STATUS: Authentik doesn't support programmatic API token creation without UI access
302+
// (see: https://github.com/goauthentik/authentik/issues/12882)
303+
//
304+
// WORKAROUND: Manual token creation via Authentik admin UI (one-time setup)
305+
//
306+
// NOTE: AUTHENTIK_BOOTSTRAP_TOKEN (from .env) is for initial admin user creation,
307+
// NOT for API access. We need a separate API token created via UI.
308+
309+
hecateEnv, err := readEnvFile("/opt/hecate/.env")
296310
if err != nil {
297-
return "", "", fmt.Errorf("failed to read /opt/bionicgpt/.env: %w\n"+
298-
"Ensure BionicGPT is installed with: eos create bionicgpt", err)
311+
return "", "", fmt.Errorf("failed to read /opt/hecate/.env: %w\n"+
312+
"Authentik configuration should be in Hecate .env file", err)
299313
}
300314

301-
// Check for AUTHENTIK_TOKEN in BionicGPT .env
302-
apiKey := bionicgptEnv["AUTHENTIK_TOKEN"]
315+
// Check for AUTHENTIK_API_TOKEN (preferred) or legacy variants
316+
apiKey := hecateEnv["AUTHENTIK_API_TOKEN"]
303317
if apiKey == "" {
304-
// Fallback: Try AUTHENTIK_API_KEY
305-
apiKey = bionicgptEnv["AUTHENTIK_API_KEY"]
318+
apiKey = hecateEnv["AUTHENTIK_TOKEN"]
306319
}
320+
if apiKey == "" {
321+
apiKey = hecateEnv["AUTHENTIK_API_KEY"]
322+
}
323+
324+
// NOTE: We do NOT fallback to AUTHENTIK_BOOTSTRAP_TOKEN - that's for initial setup,
325+
// not API access. Authentik requires a separate API token created via UI.
307326

308327
if apiKey == "" {
309-
return "", "", fmt.Errorf("AUTHENTIK_TOKEN not found in /opt/bionicgpt/.env\n" +
310-
"Add to .env file:\n" +
311-
" AUTHENTIK_TOKEN=your_authentik_api_token\n" +
312-
"Get token from: https://hera.your-domain/if/admin/#/core/tokens")
328+
return "", "", fmt.Errorf("AUTHENTIK_API_TOKEN not found in /opt/hecate/.env\n\n" +
329+
"To configure Authentik API access:\n\n" +
330+
"1. Login to Authentik admin UI:\n" +
331+
" https://hera.codemonkey.net.au/if/admin/\n\n" +
332+
"2. Login with bootstrap credentials:\n" +
333+
" Email: (from AUTHENTIK_BOOTSTRAP_EMAIL in /opt/hecate/.env)\n" +
334+
" Password: (from AUTHENTIK_BOOTSTRAP_PASSWORD in /opt/hecate/.env)\n\n" +
335+
"3. Navigate to: Directory → Tokens → Create\n" +
336+
" - User: Select your admin user\n" +
337+
" - Intent: API\n" +
338+
" - Expiry: Never (or long duration like 365 days)\n\n" +
339+
"4. Copy the generated token and add to /opt/hecate/.env:\n" +
340+
" echo 'AUTHENTIK_API_TOKEN=<your_token_here>' | sudo tee -a /opt/hecate/.env\n\n" +
341+
"5. Re-run this command\n\n" +
342+
"NOTE: This is a one-time manual step. Authentik doesn't yet support automated\n" +
343+
"API token creation (tracked in https://github.com/goauthentik/authentik/issues/12882)")
313344
}
314345

315346
// Get base URL from env or use default
316-
baseURL := bionicgptEnv["AUTHENTIK_BASE_URL"]
347+
baseURL := hecateEnv["AUTHENTIK_BASE_URL"]
317348
if baseURL == "" {
318-
// Default to localhost:9000 (Authentik default in Hecate stack)
349+
// Default to localhost:9000 (Authentik server container exposed port)
319350
baseURL = "http://localhost:9000"
320351
}
321352

pkg/hecate/bootstrap_credentials.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,14 @@ const (
1717
ConsulAuthentikBootstrapEmail = ConsulHecatePrefix + "secrets/authentik/bootstrap_email"
1818
ConsulAuthentikBootstrapPassword = ConsulHecatePrefix + "secrets/authentik/bootstrap_password"
1919
ConsulAuthentikBootstrapToken = ConsulHecatePrefix + "secrets/authentik/bootstrap_token"
20+
21+
// NOTE: AUTHENTIK_API_TOKEN is NOT stored in Consul for now
22+
// RATIONALE: API tokens are created manually via Authentik UI (upstream limitation)
23+
// STORAGE: Stored in /opt/hecate/.env (one-time manual setup)
24+
// TODO (ROADMAP - 6-12 months): Migrate to Vault when we add full Vault integration
25+
//
26+
// When we do migrate to Vault, the path should be:
27+
// ConsulAuthentikAPIToken = ConsulHecatePrefix + "secrets/authentik/api_token"
2028
)
2129

2230
// AuthentikBootstrapCredentials holds the bootstrap credentials for Authentik first-time setup

pkg/hecate/config_generator.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,14 @@ func gatherApps(rc *eos_io.RuntimeContext, previousConfig *RawYAMLConfig) (map[s
130130
logger.Info("terminal prompt: --- Add Application ---")
131131

132132
// App name
133-
logger.Info("terminal prompt: App name (e.g., main, wazuh, nextcloud) [Enter to finish]: ")
133+
logger.Info("terminal prompt: App name (e.g., main, wazuh, nextcloud, bionicgpt) [Enter to finish]: ")
134+
logger.Info("terminal prompt: ")
135+
logger.Info("terminal prompt: Suggested modules:")
136+
logger.Info("terminal prompt: • bionicgpt - AI chatbot with RAG (Retrieval-Augmented Generation)")
137+
logger.Info("terminal prompt: • wazuh - Security monitoring and SIEM platform")
138+
logger.Info("terminal prompt: • nextcloud - File sharing and collaboration")
139+
logger.Info("terminal prompt: • main - Your main application/website")
140+
logger.Info("terminal prompt: ")
134141
fmt.Print("App name: ")
135142
appName := strings.TrimSpace(mustReadLine(reader))
136143
if appName == "" {

pkg/hecate/lifecycle_compat.go

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -150,12 +150,33 @@ AUTHENTIK_TAG=%s
150150
AUTHENTIK_IMAGE=ghcr.io/goauthentik/server
151151
AUTHENTIK_WORKER__THREADS=%s
152152
153-
# Authentik Bootstrap Credentials
154-
# These are used for initial admin user creation
153+
# Authentik Bootstrap Credentials - ADMIN LOGIN CREDENTIALS
154+
# Use these to login to Authentik admin UI: https://hera.your-domain/if/admin/
155+
#
156+
# Login Email: %s
157+
# Login Password: %s
158+
#
159+
# NOTE: If these values are missing from this file, they may be stored in Consul KV.
160+
# Retrieve with: consul kv get hecate/secrets/authentik/bootstrap_email
161+
# consul kv get hecate/secrets/authentik/bootstrap_password
162+
#
155163
AUTHENTIK_BOOTSTRAP_EMAIL=%s
156164
AUTHENTIK_BOOTSTRAP_PASSWORD=%s
157165
AUTHENTIK_BOOTSTRAP_TOKEN=%s
158166
167+
# Authentik API Token (REQUIRED for automated service integration)
168+
# This token is used by 'eos update hecate --add <service>' to configure SSO automatically
169+
#
170+
# To create this token (one-time setup):
171+
# 1. Login to Authentik admin UI: https://hera.your-domain/if/admin/
172+
# 2. Use bootstrap credentials above (email + password)
173+
# 3. Navigate to: Directory → Tokens → Create
174+
# 4. Set: User = admin, Intent = API, Expiry = Never (or 365 days)
175+
# 5. Copy the generated token and replace the placeholder below
176+
#
177+
# Leave empty if you don't plan to use automated SSO integration
178+
AUTHENTIK_API_TOKEN=
179+
159180
# Compose Port Configuration
160181
COMPOSE_PORT_HTTP=%s
161182
COMPOSE_PORT_HTTPS=%s

pkg/hecate/yaml_generator.go

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -722,12 +722,33 @@ AUTHENTIK_TAG=%s
722722
AUTHENTIK_IMAGE=ghcr.io/goauthentik/server
723723
AUTHENTIK_WORKER__THREADS=%s
724724
725-
# Authentik Bootstrap Credentials
726-
# Use these to login for the first time - Email: %s, Password: %s
725+
# Authentik Bootstrap Credentials - ADMIN LOGIN CREDENTIALS
726+
# Use these to login to Authentik admin UI: https://hera.your-domain/if/admin/
727+
#
728+
# Login Email: %s
729+
# Login Password: %s
730+
#
731+
# NOTE: If these values are missing from this file, they may be stored in Consul KV.
732+
# Retrieve with: consul kv get hecate/secrets/authentik/bootstrap_email
733+
# consul kv get hecate/secrets/authentik/bootstrap_password
734+
#
727735
AUTHENTIK_BOOTSTRAP_EMAIL=%s
728736
AUTHENTIK_BOOTSTRAP_PASSWORD=%s
729737
AUTHENTIK_BOOTSTRAP_TOKEN=%s
730738
739+
# Authentik API Token (REQUIRED for automated service integration)
740+
# This token is used by 'eos update hecate --add <service>' to configure SSO automatically
741+
#
742+
# To create this token (one-time setup):
743+
# 1. Login to Authentik admin UI: https://hera.your-domain/if/admin/
744+
# 2. Use bootstrap credentials above (email + password)
745+
# 3. Navigate to: Directory → Tokens → Create
746+
# 4. Set: User = admin, Intent = API, Expiry = Never (or 365 days)
747+
# 5. Copy the generated token and replace the placeholder below
748+
#
749+
# Leave empty if you don't plan to use automated SSO integration
750+
AUTHENTIK_API_TOKEN=
751+
731752
# Ports
732753
COMPOSE_PORT_HTTP=%s
733754
COMPOSE_PORT_HTTPS=%s

0 commit comments

Comments
 (0)