Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,10 @@ Airlock provides an additional layer of security by routing all network traffic
- Local: `.copilot_here/network.json`
- Default Rules: `~/.config/copilot_here/default-airlock-rules.json` (updated with script updates)

A local `network.json` replaces the global one outright, so the two are never merged. That's why the first time `--enable-airlock` or `--disable-airlock` creates a local file in a project, it copies your global config across rather than starting empty. The command tells you when it does this.

Enabling and disabling only ever changes the `enabled` value. The commands leave your comments, key order, indentation, and any keys `copilot_here` doesn't know about exactly as you wrote them.

**Example Configuration:**
```json
{
Expand All @@ -276,7 +280,7 @@ Airlock provides an additional layer of security by routing all network traffic
- **enforce** (`e`): Blocks requests not matching the allowlist
- **monitor** (`m`): Allows all requests but logs them for review

When enabling Airlock for the first time, you'll be prompted to choose between enforce and monitor mode.
New configs start in enforce mode. Switch to monitor by setting `"mode": "monitor"` in `network.json`.

**Logging:**
When `enable_logging` is true (or in monitor mode), request logs are saved to `.copilot_here/logs/` (excluded from git by default).
Expand Down
11 changes: 3 additions & 8 deletions app/Commands/Airlock/DisableAirlock.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System.CommandLine;
using CopilotHere.Infrastructure;

namespace CopilotHere.Commands.Airlock;

Expand All @@ -8,13 +7,9 @@ public sealed partial class AirlockCommands
private static Command SetDisableAirlockCommand()
{
var command = new Command("--disable-airlock", "Disable Airlock for local config");
command.SetAction(_ =>
{
var paths = AppPaths.Resolve();
AirlockConfig.DisableLocal(paths);
Console.WriteLine("✅ Airlock disabled (local)");
return 0;
});
command.SetAction(_ => RunToggle(
"✅ Airlock disabled (local)",
paths => (AirlockConfig.DisableLocal(paths), AirlockConfig.GetLocalRulesPath(paths))));
return command;
}
}
11 changes: 3 additions & 8 deletions app/Commands/Airlock/DisableGlobalAirlock.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System.CommandLine;
using CopilotHere.Infrastructure;

namespace CopilotHere.Commands.Airlock;

Expand All @@ -8,13 +7,9 @@ public sealed partial class AirlockCommands
private static Command SetDisableGlobalAirlockCommand()
{
var command = new Command("--disable-global-airlock", "Disable Airlock for global config");
command.SetAction(_ =>
{
var paths = AppPaths.Resolve();
AirlockConfig.DisableGlobal(paths);
Console.WriteLine("✅ Airlock disabled (global)");
return 0;
});
command.SetAction(_ => RunToggle(
"✅ Airlock disabled (global)",
paths => (AirlockConfig.DisableGlobal(paths), AirlockConfig.GetGlobalRulesPath(paths))));
return command;
}
}
12 changes: 3 additions & 9 deletions app/Commands/Airlock/EnableAirlock.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System.CommandLine;
using CopilotHere.Infrastructure;

namespace CopilotHere.Commands.Airlock;

Expand All @@ -8,14 +7,9 @@ public sealed partial class AirlockCommands
private static Command SetEnableAirlockCommand()
{
var command = new Command("--enable-airlock", "Enable Airlock with local rules (.copilot_here/network.json)");
command.SetAction(_ =>
{
var paths = AppPaths.Resolve();
AirlockConfig.EnableLocal(paths);
Console.WriteLine("✅ Airlock enabled (local)");
Console.WriteLine($" 📁 Rules: {AirlockConfig.GetLocalRulesPath(paths)}");
return 0;
});
command.SetAction(_ => RunToggle(
"✅ Airlock enabled (local)",
paths => (AirlockConfig.EnableLocal(paths), AirlockConfig.GetLocalRulesPath(paths))));
return command;
}
}
12 changes: 3 additions & 9 deletions app/Commands/Airlock/EnableGlobalAirlock.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System.CommandLine;
using CopilotHere.Infrastructure;

namespace CopilotHere.Commands.Airlock;

Expand All @@ -8,14 +7,9 @@ public sealed partial class AirlockCommands
private static Command SetEnableGlobalAirlockCommand()
{
var command = new Command("--enable-global-airlock", "Enable Airlock with global rules (~/.config/copilot_here/network.json)");
command.SetAction(_ =>
{
var paths = AppPaths.Resolve();
AirlockConfig.EnableGlobal(paths);
Console.WriteLine("✅ Airlock enabled (global)");
Console.WriteLine($" 🌍 Rules: {AirlockConfig.GetGlobalRulesPath(paths)}");
return 0;
});
command.SetAction(_ => RunToggle(
"✅ Airlock enabled (global)",
paths => (AirlockConfig.EnableGlobal(paths), AirlockConfig.GetGlobalRulesPath(paths))));
return command;
}
}
7 changes: 6 additions & 1 deletion app/Commands/Airlock/NetworkConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,15 @@ public sealed class NetworkRule
/// <summary>
/// JSON source generator context for AOT-compatible serialization.
/// </summary>
// network.json is a hand-edited file, so the reader tolerates comments and trailing
// commas. Anything that loads here must also survive a toggle, and the toggle's
// Utf8JsonReader is configured to match.
[JsonSourceGenerationOptions(
WriteIndented = true,
PropertyNamingPolicy = JsonKnownNamingPolicy.SnakeCaseLower,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull)]
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
ReadCommentHandling = JsonCommentHandling.Skip,
AllowTrailingCommas = true)]
[JsonSerializable(typeof(NetworkConfig))]
[JsonSerializable(typeof(NetworkRule))]
[JsonSerializable(typeof(List<NetworkRule>))]
Expand Down
34 changes: 34 additions & 0 deletions app/Commands/Airlock/_AirlockCommands.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using System.CommandLine;
using System.Text.Json;
using CopilotHere.Infrastructure;

namespace CopilotHere.Commands.Airlock;

Expand All @@ -17,4 +19,36 @@ public void Configure(RootCommand root)
root.Add(SetEditAirlockRulesCommand());
root.Add(SetEditGlobalAirlockRulesCommand());
}

/// <summary>
/// Runs an Airlock toggle and reports which file it wrote and how.
/// </summary>
/// <param name="toggle">Returns the outcome and the path it wrote.</param>
private static int RunToggle(string successMessage, Func<AppPaths, (AirlockToggleOutcome Outcome, string Path)> toggle)
{
var paths = AppPaths.Resolve();

try
{
var (outcome, path) = toggle(paths);

Console.WriteLine(successMessage);
Console.WriteLine($" 📁 Rules: {path}");

if (outcome == AirlockToggleOutcome.SeededFromGlobal)
{
Console.WriteLine($" ↳ seeded from global config ({paths.GetGlobalPath("network.json")})");
Console.WriteLine(" Local config replaces global entirely, so its rules were copied across.");
}

return 0;
}
catch (JsonException ex)
{
Console.Error.WriteLine("❌ Could not update the Airlock config.");
Console.Error.WriteLine($" {ex.Message}");
Console.Error.WriteLine(" No changes were written.");
return 1;
}
}
}
Loading
Loading