diff --git a/src/Commands/ConfigSave.php b/src/Commands/ConfigSave.php
index a646be9..56a51ab 100644
--- a/src/Commands/ConfigSave.php
+++ b/src/Commands/ConfigSave.php
@@ -46,6 +46,8 @@
use Gitcd\Helpers\Str;
use Gitcd\Helpers\Dir;
use Gitcd\Helpers\Git;
+use Gitcd\Helpers\FileEncryption;
+use Gitcd\Helpers\Secrets;
use Gitcd\Utils\Json;
Class ConfigSave extends Command {
@@ -96,6 +98,25 @@ protected function execute(InputInterface $input, OutputInterface $output): int
return Command::SUCCESS;
}
+ // Re-encrypt .env files if encryption is configured
+ $secretsMode = Json::read('deployment.secrets', 'file', $repo_dir);
+ if ($secretsMode === 'encrypted' && Secrets::hasKey()) {
+ $unencrypted = FileEncryption::findUnencryptedEnvFiles($configrepo);
+ if (!empty($unencrypted)) {
+ $output->writeln("Encrypting .env files...");
+ $dir = rtrim($configrepo, '/') . '/';
+ foreach ($unencrypted as $envName) {
+ $envPath = $dir . $envName;
+ $encPath = $envPath . '.enc';
+ if (Secrets::encryptFile($envPath, $encPath)) {
+ $output->writeln(" ✓> Encrypted: {$envName} → {$envName}.enc");
+ } else {
+ $output->writeln(" ✗> Failed to encrypt: {$envName}");
+ }
+ }
+ }
+ }
+
Git::commit( 'Saving untracked changes', $configrepo );
Git::push( $configrepo );
$output->writeln("Your config repo was saved");
diff --git a/src/Commands/ProtocolStart.php b/src/Commands/ProtocolStart.php
index ba46c34..5d732bd 100644
--- a/src/Commands/ProtocolStart.php
+++ b/src/Commands/ProtocolStart.php
@@ -565,25 +565,26 @@ private function startContainers(string $dir, array $ctx): void
}
if ($tmpEnv) {
- $secretsFile = rtrim($dir, '/') . '/.env.protocol-secrets';
- copy($tmpEnv, $secretsFile);
- chmod($secretsFile, 0600);
- unlink($tmpEnv);
-
- $overrideFile = SecretsProvider::generateComposeOverride($composePath, $secretsFile);
+ // Keep secrets in RAM — use the tmpEnv path directly (already on /dev/shm)
+ $overrideFile = SecretsProvider::generateComposeOverride($composePath, $tmpEnv);
$runner->log("{$dockerCommand} up --build -d (with secrets)");
- Shell::run("cd " . escapeshellarg($dir)
+ $runner->log("tmpEnv={$tmpEnv} overrideFile={$overrideFile}");
+ $runner->log("override contents: " . file_get_contents($overrideFile));
+
+ $cmd = "cd " . escapeshellarg($dir)
. " && {$dockerCommand} -f " . escapeshellarg($composePath)
. " -f " . escapeshellarg($overrideFile)
. $portOverrideFlag
- . " up --build -d 2>&1");
+ . " up --build -d 2>&1";
+ $runner->log("docker cmd: {$cmd}");
+ Shell::run($cmd);
- unlink($secretsFile);
+ unlink($tmpEnv);
unlink($overrideFile);
$runner->log("Secrets temp files cleaned up");
} else {
- $runner->log("{$dockerCommand} up --build -d");
+ $runner->log("{$dockerCommand} up --build -d (NO secrets - resolveToTempFile returned null)");
Shell::run("cd " . escapeshellarg($dir)
. " && {$dockerCommand}"
. " -f " . escapeshellarg($composePath)
@@ -602,13 +603,9 @@ private function dockerUpWithSecrets(string $dir, StageRunner $runner): bool
{
$tmpEnv = SecretsProvider::resolveToTempFile($dir);
if ($tmpEnv) {
- $secretsFile = rtrim($dir, '/') . '/.env.protocol-secrets';
- copy($tmpEnv, $secretsFile);
- chmod($secretsFile, 0600);
- unlink($tmpEnv);
-
+ // Keep secrets in RAM — use the tmpEnv path directly (already on /dev/shm)
$composePath = rtrim($dir, '/') . '/docker-compose.yml';
- $overrideFile = SecretsProvider::generateComposeOverride($composePath, $secretsFile);
+ $overrideFile = SecretsProvider::generateComposeOverride($composePath, $tmpEnv);
$envFile = rtrim($dir, '/') . '/.env.deployment';
$dockerCommand = Docker::getDockerCommand();
@@ -621,7 +618,7 @@ private function dockerUpWithSecrets(string $dir, StageRunner $runner): bool
. " up -d 2>&1", $returnVar);
$started = $returnVar === 0;
- unlink($secretsFile);
+ unlink($tmpEnv);
unlink($overrideFile);
$runner->log("Secrets temp files cleaned up");
return $started;
diff --git a/src/Helpers/SecretsProvider.php b/src/Helpers/SecretsProvider.php
index cba909a..d6d23ba 100644
--- a/src/Helpers/SecretsProvider.php
+++ b/src/Helpers/SecretsProvider.php
@@ -54,14 +54,17 @@ class SecretsProvider
public static function resolveToTempFile(string $repoDir): ?string
{
$mode = Json::read('deployment.secrets', 'file', $repoDir);
+ self::log("resolveToTempFile: mode={$mode} repoDir={$repoDir}");
if ($mode === 'encrypted') {
$configRepo = Config::repo($repoDir);
if (!$configRepo) {
+ self::log("resolveToTempFile: no config repo found");
return null;
}
$encFile = $configRepo . '.env.enc';
if (!is_file($encFile) || !Secrets::hasKey()) {
+ self::log("resolveToTempFile: enc file missing or no key");
return null;
}
return Secrets::decryptToTempFile($encFile);
@@ -70,15 +73,19 @@ public static function resolveToTempFile(string $repoDir): ?string
if ($mode === 'aws') {
// Dynamic load to avoid hard dependency on the plugin
if (!class_exists('\\Gitcd\\Plugins\\awssecrets\\AwsSecretsHelper')) {
+ self::log("resolveToTempFile: AwsSecretsHelper class not found");
return null;
}
// Pull secrets from AWS
$awsEnv = \Gitcd\Plugins\awssecrets\AwsSecretsHelper::pullSecret($repoDir);
if ($awsEnv === null) {
+ self::log("resolveToTempFile: AWS pullSecret returned null");
return null;
}
+ self::log("resolveToTempFile: AWS pull OK, got " . strlen($awsEnv) . " bytes");
+
// Read the existing .env (non-secret config like LOG_LEVEL, APP_ENV)
$baseEnv = self::readBaseEnv($repoDir);
@@ -91,12 +98,28 @@ public static function resolveToTempFile(string $repoDir): ?string
file_put_contents($tmpFile, $merged);
chmod($tmpFile, 0600);
+ self::log("resolveToTempFile: wrote {$tmpFile}");
return $tmpFile;
}
+ self::log("resolveToTempFile: mode '{$mode}' not handled, returning null");
return null;
}
+ /**
+ * Write a line to the secrets debug log.
+ */
+ private static function log(string $message): void
+ {
+ $logDir = '/var/log/protocol';
+ if (!is_dir($logDir)) {
+ @mkdir($logDir, 0755, true);
+ }
+ $logFile = $logDir . '/secrets-provider.log';
+ $line = '[' . date('Y-m-d H:i:s') . '] ' . $message . "\n";
+ file_put_contents($logFile, $line, FILE_APPEND | LOCK_EX);
+ }
+
/**
* Generate a docker-compose override file that adds env_file to every service.
*
@@ -113,14 +136,14 @@ public static function generateComposeOverride(string $composePath, string $envF
// Parse the original compose file to get service names
$parsed = Yaml::parseFile($composePath);
$services = $parsed['services'] ?? [];
- $envFileName = basename($envFilePath);
+ $envFileRef = realpath($envFilePath) ?: $envFilePath;
// Build override YAML
$override = "services:\n";
foreach (array_keys($services) as $serviceName) {
$override .= " {$serviceName}:\n";
$override .= " env_file:\n";
- $override .= " - {$envFileName}\n";
+ $override .= " - {$envFileRef}\n";
}
$overridePath = dirname($composePath) . '/.docker-compose.secrets-override.yml';