@s-gw/s-gw 0.1.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 (123) hide show
  1. package/.codex-plugin/plugin.json +35 -0
  2. package/.mcp.json +16 -0
  3. package/LICENSE +201 -0
  4. package/NOTICE +7 -0
  5. package/README.md +197 -0
  6. package/TRADEMARKS.md +9 -0
  7. package/assets/icons/aws-ec2.png +0 -0
  8. package/assets/icons/lucide/bot.svg +8 -0
  9. package/assets/icons/lucide/monitor.svg +5 -0
  10. package/assets/icons/lucide/server.svg +6 -0
  11. package/assets/icons/lucide/terminal.svg +4 -0
  12. package/assets/icons/s-gw-128.png +0 -0
  13. package/assets/icons/s-gw-16.png +0 -0
  14. package/assets/icons/s-gw-180.png +0 -0
  15. package/assets/icons/s-gw-192.png +0 -0
  16. package/assets/icons/s-gw-32.png +0 -0
  17. package/assets/icons/s-gw-64.png +0 -0
  18. package/assets/icons/s-gw-menu-bar-template.png +0 -0
  19. package/dist/agent-context.d.ts +17 -0
  20. package/dist/agent-context.js +207 -0
  21. package/dist/agents.d.ts +64 -0
  22. package/dist/agents.js +763 -0
  23. package/dist/cli.d.ts +2 -0
  24. package/dist/cli.js +1385 -0
  25. package/dist/command-suggest.d.ts +3 -0
  26. package/dist/command-suggest.js +131 -0
  27. package/dist/console-server.d.ts +16 -0
  28. package/dist/console-server.js +978 -0
  29. package/dist/console-ui/assets/codex-DYTPdPxi.png +0 -0
  30. package/dist/console-ui/assets/cursor-CBrUTJD-.png +0 -0
  31. package/dist/console-ui/assets/geist-cyrillic-ext-wght-normal-DjL33-gN.woff2 +0 -0
  32. package/dist/console-ui/assets/geist-cyrillic-wght-normal-BEAKL7Jp.woff2 +0 -0
  33. package/dist/console-ui/assets/geist-latin-ext-wght-normal-DC-KSUi6.woff2 +0 -0
  34. package/dist/console-ui/assets/geist-latin-wght-normal-BgDaEnEv.woff2 +0 -0
  35. package/dist/console-ui/assets/geist-vietnamese-wght-normal-6IgcOCM7.woff2 +0 -0
  36. package/dist/console-ui/assets/hermes-B8hNbJPm.png +0 -0
  37. package/dist/console-ui/assets/index-BxUf0Sye.js +96 -0
  38. package/dist/console-ui/assets/index-CmTiBR_w.css +2 -0
  39. package/dist/console-ui/assets/omnigent-Cxa4p2Mq.png +0 -0
  40. package/dist/console-ui/assets/openclaw-C5wL4ZVW.png +0 -0
  41. package/dist/console-ui/assets/opencode-D_wFATSC.png +0 -0
  42. package/dist/console-ui/assets/openhands-DnrlGgev.svg +9 -0
  43. package/dist/console-ui/assets/s-gw-64-ByMUGQ3K.png +0 -0
  44. package/dist/console-ui/assets/vscode-Bdtr9eyf.png +0 -0
  45. package/dist/console-ui/assets/zeptoclaw-DztQW8Sw.png +0 -0
  46. package/dist/console-ui/index.html +13 -0
  47. package/dist/crypto.d.ts +6 -0
  48. package/dist/crypto.js +53 -0
  49. package/dist/executor.d.ts +7 -0
  50. package/dist/executor.js +297 -0
  51. package/dist/gateway.d.ts +31 -0
  52. package/dist/gateway.js +114 -0
  53. package/dist/guard.d.ts +61 -0
  54. package/dist/guard.js +247 -0
  55. package/dist/install.d.ts +146 -0
  56. package/dist/install.js +629 -0
  57. package/dist/mcp-server.d.ts +2 -0
  58. package/dist/mcp-server.js +119 -0
  59. package/dist/native/s-gw-core +0 -0
  60. package/dist/native/s-gw-keychain-helper +0 -0
  61. package/dist/onepassword.d.ts +48 -0
  62. package/dist/onepassword.js +412 -0
  63. package/dist/paths.d.ts +4 -0
  64. package/dist/paths.js +22 -0
  65. package/dist/s-gw Menu Bar.app/Contents/Info.plist +28 -0
  66. package/dist/s-gw Menu Bar.app/Contents/MacOS/s-gw-menu-bar-helper +0 -0
  67. package/dist/s-gw Menu Bar.app/Contents/Resources/AppIcon.icns +0 -0
  68. package/dist/s-gw Menu Bar.app/Contents/Resources/AwsEc2.png +0 -0
  69. package/dist/s-gw Menu Bar.app/Contents/Resources/Lucide-bot.svg +8 -0
  70. package/dist/s-gw Menu Bar.app/Contents/Resources/Lucide-monitor.svg +5 -0
  71. package/dist/s-gw Menu Bar.app/Contents/Resources/Lucide-server.svg +6 -0
  72. package/dist/s-gw Menu Bar.app/Contents/Resources/Lucide-terminal.svg +4 -0
  73. package/dist/s-gw Menu Bar.app/Contents/Resources/MenuBarTemplate.png +0 -0
  74. package/dist/s-gw Menu Bar.app/Contents/_CodeSignature/CodeResources +194 -0
  75. package/dist/s-gw.app/Contents/Info.plist +28 -0
  76. package/dist/s-gw.app/Contents/MacOS/s-gw +0 -0
  77. package/dist/s-gw.app/Contents/Resources/AppIcon.icns +0 -0
  78. package/dist/s-gw.app/Contents/Resources/MenuBarTemplate.png +0 -0
  79. package/dist/s-gw.app/Contents/_CodeSignature/CodeResources +139 -0
  80. package/dist/scanner.d.ts +9 -0
  81. package/dist/scanner.js +437 -0
  82. package/dist/ssh.d.ts +31 -0
  83. package/dist/ssh.js +286 -0
  84. package/dist/store.d.ts +131 -0
  85. package/dist/store.js +1611 -0
  86. package/dist/types.d.ts +196 -0
  87. package/dist/types.js +2 -0
  88. package/dist/unlock.d.ts +29 -0
  89. package/dist/unlock.js +274 -0
  90. package/dist/windows/VERSION.txt +1 -0
  91. package/dist/windows/s-gw-client.cmd +4 -0
  92. package/dist/windows/s-gw-client.ps1 +106 -0
  93. package/dist/windows/s-gw-credential.cmd +4 -0
  94. package/dist/windows/s-gw-credential.ps1 +167 -0
  95. package/dist/windows/s-gw-helper.cmd +4 -0
  96. package/dist/windows/s-gw-helper.ps1 +180 -0
  97. package/docs/README.md +23 -0
  98. package/docs/agents.md +160 -0
  99. package/docs/architecture.md +72 -0
  100. package/docs/deployment.md +447 -0
  101. package/docs/detection.md +44 -0
  102. package/docs/images/s-gw-overview.png +0 -0
  103. package/docs/integrations.md +195 -0
  104. package/docs/keychain.md +39 -0
  105. package/docs/onepassword.md +84 -0
  106. package/docs/quickstart.md +104 -0
  107. package/docs/threat-model.md +100 -0
  108. package/docs/ui/THIRD_PARTY_NOTICES.md +111 -0
  109. package/docs/ui/apple-touch-icon.png +0 -0
  110. package/docs/ui/favicon-32.png +0 -0
  111. package/docs/ui/local-console.html +4477 -0
  112. package/docs/ui/vendor/d3-sankey/d3-array.LICENSE.txt +27 -0
  113. package/docs/ui/vendor/d3-sankey/d3-array.min.js +2 -0
  114. package/docs/ui/vendor/d3-sankey/d3-path.LICENSE.txt +27 -0
  115. package/docs/ui/vendor/d3-sankey/d3-path.min.js +2 -0
  116. package/docs/ui/vendor/d3-sankey/d3-sankey.LICENSE.txt +27 -0
  117. package/docs/ui/vendor/d3-sankey/d3-sankey.min.js +2 -0
  118. package/docs/ui/vendor/d3-sankey/d3-shape.LICENSE.txt +27 -0
  119. package/docs/ui/vendor/d3-sankey/d3-shape.min.js +2 -0
  120. package/docs/ui/vendor/sankeymatic/LICENSE.txt +17 -0
  121. package/docs/ui/vendor/sankeymatic/sankey.js +897 -0
  122. package/package.json +117 -0
  123. package/skills/s-gw/SKILL.md +19 -0
@@ -0,0 +1,167 @@
1
+ [CmdletBinding()]
2
+ param(
3
+ [Parameter(Mandatory = $true, Position = 0)]
4
+ [ValidateSet("get", "set", "delete", "status")]
5
+ [string]$Command,
6
+
7
+ [Parameter(Mandatory = $true)]
8
+ [string]$Service,
9
+
10
+ [Parameter(Mandatory = $true)]
11
+ [string]$Account,
12
+
13
+ [string]$Label = "s-gw local secret"
14
+ )
15
+
16
+ $ErrorActionPreference = "Stop"
17
+
18
+ if (-not $IsWindows -and $PSVersionTable.PSEdition -eq "Core") {
19
+ throw "Windows Credential Manager is only available on Windows."
20
+ }
21
+
22
+ Add-Type -TypeDefinition @"
23
+ using System;
24
+ using System.Runtime.InteropServices;
25
+
26
+ public static class SgwCredMan {
27
+ public const UInt32 CRED_TYPE_GENERIC = 1;
28
+ public const UInt32 CRED_PERSIST_LOCAL_MACHINE = 2;
29
+
30
+ [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
31
+ public struct CREDENTIAL {
32
+ public UInt32 Flags;
33
+ public UInt32 Type;
34
+ public string TargetName;
35
+ public string Comment;
36
+ public System.Runtime.InteropServices.ComTypes.FILETIME LastWritten;
37
+ public UInt32 CredentialBlobSize;
38
+ public IntPtr CredentialBlob;
39
+ public UInt32 Persist;
40
+ public UInt32 AttributeCount;
41
+ public IntPtr Attributes;
42
+ public string TargetAlias;
43
+ public string UserName;
44
+ }
45
+
46
+ [DllImport("advapi32.dll", EntryPoint = "CredReadW", CharSet = CharSet.Unicode, SetLastError = true)]
47
+ public static extern bool CredRead(string target, UInt32 type, UInt32 flags, out IntPtr credentialPtr);
48
+
49
+ [DllImport("advapi32.dll", EntryPoint = "CredWriteW", CharSet = CharSet.Unicode, SetLastError = true)]
50
+ public static extern bool CredWrite(ref CREDENTIAL credential, UInt32 flags);
51
+
52
+ [DllImport("advapi32.dll", EntryPoint = "CredDeleteW", CharSet = CharSet.Unicode, SetLastError = true)]
53
+ public static extern bool CredDelete(string target, UInt32 type, UInt32 flags);
54
+
55
+ [DllImport("advapi32.dll", EntryPoint = "CredFree", SetLastError = true)]
56
+ public static extern void CredFree(IntPtr buffer);
57
+ }
58
+ "@
59
+
60
+ function Get-TargetName {
61
+ return "s-gw/$Service/$Account"
62
+ }
63
+
64
+ function Get-LastErrorCode {
65
+ return [Runtime.InteropServices.Marshal]::GetLastWin32Error()
66
+ }
67
+
68
+ function Throw-Win32([string]$Action) {
69
+ $code = Get-LastErrorCode
70
+ $message = (New-Object ComponentModel.Win32Exception($code)).Message
71
+ throw "$Action failed ($code): $message"
72
+ }
73
+
74
+ function Read-CredentialValue([string]$Target) {
75
+ $ptr = [IntPtr]::Zero
76
+ if (-not [SgwCredMan]::CredRead($Target, [SgwCredMan]::CRED_TYPE_GENERIC, 0, [ref]$ptr)) {
77
+ $code = Get-LastErrorCode
78
+ if ($code -eq 1168) {
79
+ exit 44
80
+ }
81
+ Throw-Win32 "CredRead"
82
+ }
83
+
84
+ try {
85
+ $cred = [Runtime.InteropServices.Marshal]::PtrToStructure($ptr, [type][SgwCredMan+CREDENTIAL])
86
+ $bytes = New-Object byte[] $cred.CredentialBlobSize
87
+ [Runtime.InteropServices.Marshal]::Copy($cred.CredentialBlob, $bytes, 0, $bytes.Length)
88
+ return [Text.Encoding]::Unicode.GetString($bytes).TrimEnd([char]0)
89
+ } finally {
90
+ [SgwCredMan]::CredFree($ptr)
91
+ }
92
+ }
93
+
94
+ function Test-CredentialValue([string]$Target) {
95
+ $ptr = [IntPtr]::Zero
96
+ if (-not [SgwCredMan]::CredRead($Target, [SgwCredMan]::CRED_TYPE_GENERIC, 0, [ref]$ptr)) {
97
+ return $false
98
+ }
99
+
100
+ [SgwCredMan]::CredFree($ptr)
101
+ return $true
102
+ }
103
+
104
+ function Write-CredentialValue([string]$Target, [string]$Value) {
105
+ if (-not $Value) {
106
+ throw "Cannot store an empty Credential Manager value."
107
+ }
108
+
109
+ $bytes = [Text.Encoding]::Unicode.GetBytes($Value)
110
+ $pinned = [Runtime.InteropServices.GCHandle]::Alloc($bytes, [Runtime.InteropServices.GCHandleType]::Pinned)
111
+
112
+ try {
113
+ $cred = New-Object "SgwCredMan+CREDENTIAL"
114
+ $cred.Type = [SgwCredMan]::CRED_TYPE_GENERIC
115
+ $cred.TargetName = $Target
116
+ $cred.Comment = $Label
117
+ $cred.CredentialBlobSize = [uint32]$bytes.Length
118
+ $cred.CredentialBlob = $pinned.AddrOfPinnedObject()
119
+ $cred.Persist = [SgwCredMan]::CRED_PERSIST_LOCAL_MACHINE
120
+ $cred.UserName = "$env:USERDOMAIN\$env:USERNAME"
121
+
122
+ if (-not [SgwCredMan]::CredWrite([ref]$cred, 0)) {
123
+ Throw-Win32 "CredWrite"
124
+ }
125
+ } finally {
126
+ $pinned.Free()
127
+ }
128
+ }
129
+
130
+ function Remove-CredentialValue([string]$Target) {
131
+ if ([SgwCredMan]::CredDelete($Target, [SgwCredMan]::CRED_TYPE_GENERIC, 0)) {
132
+ return $true
133
+ }
134
+
135
+ $code = Get-LastErrorCode
136
+ if ($code -eq 1168) {
137
+ return $false
138
+ }
139
+ Throw-Win32 "CredDelete"
140
+ }
141
+
142
+ $target = Get-TargetName
143
+
144
+ switch ($Command) {
145
+ "get" {
146
+ [Console]::Out.Write((Read-CredentialValue $target))
147
+ break
148
+ }
149
+ "set" {
150
+ $value = [Console]::In.ReadToEnd()
151
+ Write-CredentialValue -Target $target -Value $value
152
+ break
153
+ }
154
+ "delete" {
155
+ $deleted = Remove-CredentialValue $target
156
+ [Console]::Out.WriteLine((@{ deleted = $deleted } | ConvertTo-Json -Compress))
157
+ break
158
+ }
159
+ "status" {
160
+ [Console]::Out.WriteLine((@{
161
+ supported = $true
162
+ target = $target
163
+ configured = Test-CredentialValue $target
164
+ } | ConvertTo-Json -Compress))
165
+ break
166
+ }
167
+ }
@@ -0,0 +1,4 @@
1
+ @echo off
2
+ setlocal
3
+ powershell.exe -NoProfile -ExecutionPolicy Bypass -File "%~dp0s-gw-helper.ps1" %*
4
+ exit /b %ERRORLEVEL%
@@ -0,0 +1,180 @@
1
+ [CmdletBinding()]
2
+ param(
3
+ [int]$Port = 8718,
4
+ [string]$ConsoleUrl = ""
5
+ )
6
+
7
+ $ErrorActionPreference = "Stop"
8
+
9
+ Add-Type -AssemblyName System.Windows.Forms
10
+ Add-Type -AssemblyName System.Drawing
11
+
12
+ function Resolve-CliPath {
13
+ if ($env:SGW_CLI_PATH -and (Test-Path -LiteralPath $env:SGW_CLI_PATH)) {
14
+ return (Resolve-Path -LiteralPath $env:SGW_CLI_PATH).Path
15
+ }
16
+
17
+ $distDir = Split-Path -Parent $PSScriptRoot
18
+ $candidate = Join-Path $distDir "cli.js"
19
+ if (Test-Path -LiteralPath $candidate) {
20
+ return (Resolve-Path -LiteralPath $candidate).Path
21
+ }
22
+
23
+ throw "Unable to find s-gw CLI. Set SGW_CLI_PATH to dist\cli.js."
24
+ }
25
+
26
+ function Resolve-NodePath {
27
+ if ($env:SGW_NODE_PATH) {
28
+ return $env:SGW_NODE_PATH
29
+ }
30
+ return "node"
31
+ }
32
+
33
+ function New-ConsoleUrl {
34
+ if ($ConsoleUrl.Trim()) {
35
+ return $ConsoleUrl.Trim().TrimEnd("/") + "/"
36
+ }
37
+ return "http://127.0.0.1:$Port/"
38
+ }
39
+
40
+ function Quote-Arg([string]$Value) {
41
+ if ($Value.Length -eq 0) {
42
+ return '""'
43
+ }
44
+ return '"' + $Value.Replace('"', '\"') + '"'
45
+ }
46
+
47
+ function Invoke-CliJson([string[]]$Args) {
48
+ $psi = New-Object System.Diagnostics.ProcessStartInfo
49
+ $psi.FileName = $script:NodePath
50
+ $allArgs = @($script:CliPath) + $Args
51
+ $psi.Arguments = ($allArgs | ForEach-Object { Quote-Arg ([string]$_) }) -join " "
52
+ $psi.UseShellExecute = $false
53
+ $psi.RedirectStandardOutput = $true
54
+ $psi.RedirectStandardError = $true
55
+ $psi.CreateNoWindow = $true
56
+
57
+ $proc = [System.Diagnostics.Process]::Start($psi)
58
+ $stdout = $proc.StandardOutput.ReadToEnd()
59
+ $stderr = $proc.StandardError.ReadToEnd()
60
+ $proc.WaitForExit()
61
+ if ($proc.ExitCode -ne 0) {
62
+ throw ($stderr.Trim() + " " + $stdout.Trim()).Trim()
63
+ }
64
+ if (-not $stdout.Trim()) {
65
+ return $null
66
+ }
67
+ return $stdout | ConvertFrom-Json
68
+ }
69
+
70
+ function Start-Client([string]$Path = "") {
71
+ $clientScript = Join-Path $PSScriptRoot "s-gw-client.ps1"
72
+ $url = $script:BaseConsoleUrl
73
+ if ($Path) {
74
+ $url = $script:BaseConsoleUrl.TrimEnd("/") + "/" + $Path.TrimStart("/")
75
+ }
76
+ Start-Process `
77
+ -FilePath "powershell.exe" `
78
+ -ArgumentList @("-NoProfile", "-ExecutionPolicy", "Bypass", "-File", $clientScript, "-Port", [string]$Port, "-ConsoleUrl", $url) | Out-Null
79
+ }
80
+
81
+ function Get-PendingRequests {
82
+ try {
83
+ $pending = Invoke-CliJson @("requests", "--state", "pending", "--limit", "25")
84
+ if ($null -eq $pending) {
85
+ return @()
86
+ }
87
+ if ($pending -is [array]) {
88
+ return $pending
89
+ }
90
+ return @($pending)
91
+ } catch {
92
+ $script:LastError = $_.Exception.Message
93
+ return @()
94
+ }
95
+ }
96
+
97
+ function Add-MenuItem([System.Windows.Forms.ContextMenuStrip]$Menu, [string]$Text, [scriptblock]$Handler, [bool]$Enabled = $true) {
98
+ $item = New-Object System.Windows.Forms.ToolStripMenuItem
99
+ $item.Text = $Text
100
+ $item.Enabled = $Enabled
101
+ if ($Handler) {
102
+ $item.Add_Click($Handler)
103
+ }
104
+ [void]$Menu.Items.Add($item)
105
+ return $item
106
+ }
107
+
108
+ function Approve-FirstPending {
109
+ $pending = Get-PendingRequests
110
+ if ($pending.Count -eq 0) {
111
+ return
112
+ }
113
+ $request = $pending[$pending.Count - 1]
114
+ Invoke-CliJson @("approve", [string]$request.id) | Out-Null
115
+ $script:Notify.ShowBalloonTip(2500, "s-gw", "Approved $($request.id)", [System.Windows.Forms.ToolTipIcon]::Info)
116
+ Update-Menu
117
+ }
118
+
119
+ function Deny-FirstPending {
120
+ $pending = Get-PendingRequests
121
+ if ($pending.Count -eq 0) {
122
+ return
123
+ }
124
+ $request = $pending[$pending.Count - 1]
125
+ Invoke-CliJson @("deny", [string]$request.id) | Out-Null
126
+ $script:Notify.ShowBalloonTip(2500, "s-gw", "Denied $($request.id)", [System.Windows.Forms.ToolTipIcon]::Info)
127
+ Update-Menu
128
+ }
129
+
130
+ function Update-Menu {
131
+ $pending = Get-PendingRequests
132
+ $count = $pending.Count
133
+ $script:Menu.Items.Clear()
134
+
135
+ $status = if ($script:LastError) { "s-gw helper - check setup" } elseif ($count -eq 1) { "1 approval waiting" } else { "$count approvals waiting" }
136
+ Add-MenuItem $script:Menu $status $null $false | Out-Null
137
+ [void]$script:Menu.Items.Add((New-Object System.Windows.Forms.ToolStripSeparator))
138
+
139
+ Add-MenuItem $script:Menu "Open s-gw" { Start-Client } | Out-Null
140
+ Add-MenuItem $script:Menu "Approve Queue" { Start-Client "approvals" } | Out-Null
141
+ Add-MenuItem $script:Menu "Refresh" { $script:LastError = ""; Update-Menu } | Out-Null
142
+ Add-MenuItem $script:Menu "Approve oldest request" { Approve-FirstPending } ($count -gt 0) | Out-Null
143
+ Add-MenuItem $script:Menu "Deny oldest request" { Deny-FirstPending } ($count -gt 0) | Out-Null
144
+ [void]$script:Menu.Items.Add((New-Object System.Windows.Forms.ToolStripSeparator))
145
+ Add-MenuItem $script:Menu "Quit helper" {
146
+ $script:Timer.Stop()
147
+ $script:Notify.Visible = $false
148
+ $script:Notify.Dispose()
149
+ [System.Windows.Forms.Application]::Exit()
150
+ } | Out-Null
151
+
152
+ $script:Notify.Text = if ($count -gt 0) { "s-gw - $count approval(s) waiting" } else { "s-gw - no pending approvals" }
153
+ if ($script:LastPendingCount -ge 0 -and $count -gt $script:LastPendingCount) {
154
+ $script:Notify.ShowBalloonTip(3000, "s-gw approval required", "$count request(s) waiting for local approval.", [System.Windows.Forms.ToolTipIcon]::Info)
155
+ }
156
+ $script:LastPendingCount = $count
157
+ }
158
+
159
+ $script:CliPath = Resolve-CliPath
160
+ $script:NodePath = Resolve-NodePath
161
+ $script:BaseConsoleUrl = New-ConsoleUrl
162
+ $script:LastError = ""
163
+ $script:LastPendingCount = -1
164
+
165
+ [System.Windows.Forms.Application]::EnableVisualStyles()
166
+ $script:Menu = New-Object System.Windows.Forms.ContextMenuStrip
167
+ $script:Notify = New-Object System.Windows.Forms.NotifyIcon
168
+ $script:Notify.Icon = [System.Drawing.SystemIcons]::Shield
169
+ $script:Notify.Text = "s-gw"
170
+ $script:Notify.Visible = $true
171
+ $script:Notify.ContextMenuStrip = $script:Menu
172
+ $script:Notify.Add_DoubleClick({ Start-Client "approvals" })
173
+
174
+ $script:Timer = New-Object System.Windows.Forms.Timer
175
+ $script:Timer.Interval = 15000
176
+ $script:Timer.Add_Tick({ Update-Menu })
177
+
178
+ Update-Menu
179
+ $script:Timer.Start()
180
+ [System.Windows.Forms.Application]::Run()
package/docs/README.md ADDED
@@ -0,0 +1,23 @@
1
+ # s-gw Documentation
2
+
3
+ ## Start Here
4
+
5
+ - [Quick start](quickstart.md): build s-gw and run the disposable trust-loop demo.
6
+ - [Architecture](architecture.md): components, data flow, and local process boundaries.
7
+ - [Threat model](threat-model.md): intended guarantees, attacker assumptions, and non-goals.
8
+ - [Deployment](deployment.md): platform support, setup, packaging, upgrade, and uninstall.
9
+
10
+ ## Integrations
11
+
12
+ - [Agent integrations](integrations.md): Codex, Claude Code, Cursor, OpenCode, and VS Code configuration.
13
+ - [Agent profiles](agents.md): known clients, aliases, config paths, and compatibility status.
14
+ - [macOS Keychain and Windows Credential Manager](keychain.md): operating system credential-store handles.
15
+ - [1Password](onepassword.md): optional `op://` reference-backed handles.
16
+
17
+ ## Security Details
18
+
19
+ - [Secret detection](detection.md): local credential-pattern scanning and tokenization.
20
+ - [Security policy](../SECURITY.md): private vulnerability reporting.
21
+ - [Third-party notices](ui/THIRD_PARTY_NOTICES.md): bundled code, artwork, and licenses.
22
+
23
+ The native app and local console expose the same local store and CLI behavior. Documentation should describe supported behavior rather than a specific UI layout unless the distinction matters.
package/docs/agents.md ADDED
@@ -0,0 +1,160 @@
1
+ # Agent Support
2
+
3
+ s-gw maintains a small profile for each known coding agent: canonical name, aliases, configuration paths, MCP shape, skills or plugin directories, and limitations. The registry structure is informed by DefenseClaw's connector model.
4
+
5
+ This does not turn s-gw into a remote guardrail proxy. s-gw stays local and uses MCP tools plus local approval to keep raw credentials out of the agent.
6
+
7
+ ## CLI
8
+
9
+ List known profiles:
10
+
11
+ ```bash
12
+ s-gw agent list
13
+ ```
14
+
15
+ Show one profile with an MCP snippet:
16
+
17
+ ```bash
18
+ s-gw agent show codex
19
+ s-gw agent show openclaw
20
+ s-gw agent show claude-code
21
+ ```
22
+
23
+ Render only the snippet:
24
+
25
+ ```bash
26
+ s-gw agent mcp-snippet codex
27
+ s-gw agent mcp-snippet openclaw
28
+ s-gw agent mcp-snippet cursor
29
+ ```
30
+
31
+ Show the Project CodeGuard hardening path for an agent:
32
+
33
+ ```bash
34
+ s-gw agent codeguard-plan codex
35
+ s-gw agent codeguard-plan claude-code
36
+ s-gw agent codeguard-plan opencode
37
+ ```
38
+
39
+ For source builds or development, override the command:
40
+
41
+ ```bash
42
+ s-gw agent mcp-snippet codex \
43
+ --command node \
44
+ --arg /path/to/s-gw/dist/mcp-server.js \
45
+ --env SGW_HOME=~/.s-gw
46
+ ```
47
+
48
+ The packaged default command is:
49
+
50
+ ```bash
51
+ s-gw-mcp
52
+ ```
53
+
54
+ ## Guard Mode
55
+
56
+ Use guard mode when launching a CLI coding agent from the terminal:
57
+
58
+ ```bash
59
+ s-gw guard status
60
+ s-gw run codex --dry-run -- -v
61
+ s-gw run codex -- --ask-for-approval never
62
+ s-gw guard run claude-code -- --help
63
+ ```
64
+
65
+ Guard mode scans the launch environment, stores credential-looking values in the local encrypted ledger during a real run, and replaces the child process values with `<<SGW_SECRET:...>>` handles. It also sets `SGW_GUARD_MODE`, `SGW_GUARD_AGENT`, `SGW_GUARD_INSTRUCTIONS`, and `SGW_GUARD_TOKENIZED_ENV` for the launched process.
66
+
67
+ Use `--command CMD` for agents without a safe default CLI launcher:
68
+
69
+ ```bash
70
+ s-gw guard run cursor --command cursor --dry-run
71
+ ```
72
+
73
+ `--dry-run` uses preview handles and does not write to the store. A real run may use `--allow-command CMD` to attach an initial approved-execution policy to any handles created from environment variables, but production policies should remain narrow.
74
+
75
+ ## Agent Profile Registry
76
+
77
+ s-gw tracks the 13 first-class agents documented by DefenseClaw as of tag `0.8.3`: OpenClaw, ZeptoClaw, Claude Code, Codex, Hermes, Cursor, Windsurf, Gemini CLI, GitHub Copilot CLI, OpenHands, Antigravity, OpenCode, and OmniGent.
78
+
79
+ Status is intentionally strict. `Supported` means s-gw has a documented stdio MCP path for the agent. `Profiled/manual` means s-gw knows the likely local config surface and can render a snippet, but setup should stay manual and should not be described as fully compatible until the app has passed a hands-on smoke test with that agent. `Planned/profiled` means the connector shape is useful for roadmap and UI inventory, but s-gw should not emit a normal MCP install snippet.
80
+
81
+ | Profile | Agent | Status | MCP config surface | Notes |
82
+ | --- | --- | --- | --- | --- |
83
+ | `openclaw` | OpenClaw | Profiled/manual | `~/.openclaw/openclaw.json` | Manual write path; prefer OpenClaw config command/UI when available. |
84
+ | `zeptoclaw` | ZeptoClaw | Profiled/manual | `~/.zeptoclaw/config.json`, `./.mcp.json` | Manual write path because ZeptoClaw owns config autosave. |
85
+ | `claudecode` | Claude Code | Supported | `./.mcp.json` (project), `~/.claude.json` via `claude mcp add` | Standard stdio MCP. Use `claude mcp add` or `.mcp.json`, not `settings.json`. Alias: `claude-code`. |
86
+ | `codex` | Codex | Supported | `~/.codex/config.toml`, `./.mcp.json` | Standard stdio MCP and packaged Codex plugin. |
87
+ | `hermes` | Hermes Agent | Profiled/manual | `~/.hermes/config.yaml` | YAML snippet; no automatic patcher yet. |
88
+ | `cursor` | Cursor | Supported | `./.cursor/mcp.json`, `~/.cursor/mcp.json` | Project-local config preferred. |
89
+ | `windsurf` | Windsurf | Profiled/manual | `~/.codeium/windsurf/mcp_config.json`, `~/.codeium/windsurf/mcp.json` | Do not create guessed config paths. |
90
+ | `geminicli` | Gemini CLI | Supported | `~/.gemini/settings.json`, `./.gemini/settings.json` | Alias: `gemini`. |
91
+ | `copilot` | GitHub Copilot CLI | Supported | `~/.copilot/mcp-config.json`, `./.github/mcp.json`, `./.mcp.json` | Workspace config preferred. |
92
+ | `openhands` | OpenHands | Profiled/manual | `~/.openhands/mcp.json` | Global MCP config; optional workspace hooks only when intentionally scoped. |
93
+ | `antigravity` | Antigravity | Profiled/manual | `~/.gemini/config/mcp_config.json`, `./.agents/mcp_config.json` | MCP config is separate from global hook config. Alias: `agy`. |
94
+ | `opencode` | OpenCode | Supported | `~/.config/opencode/opencode.json`, `opencode.json`, `.jsonc` variants | Top-level `mcp` map, not `mcpServers`. |
95
+ | `omnigent` | OmniGent | Planned/profiled | `$OMNIGENT_CONFIG_HOME/config.yaml`, `~/.omnigent/config.yaml` | DefenseClaw uses a custom policy bridge, not a normal MCP server entry. |
96
+
97
+ The registry also includes this general MCP client profile:
98
+
99
+ | Profile | Agent | Status | MCP config surface |
100
+ | --- | --- | --- | --- |
101
+ | `vscode` | VS Code / GitHub Copilot Agent Mode | Supported | `./.vscode/mcp.json` |
102
+
103
+ ## Project CodeGuard Layer
104
+
105
+ Project CodeGuard is useful as a companion layer, not a replacement for s-gw. CodeGuard gives agents secure coding guidance and review rules; s-gw keeps credentials local, tokenizes them into handles, and requires local authorization before secret-backed actions run.
106
+
107
+ The current CodeGuard project is under CoSAI/OASIS at `https://github.com/cosai-oasis/project-codeguard`. s-gw does not vendor CodeGuard's CC BY rule content; it points users to upstream release artifacts so rules can be installed, pinned, and audited intentionally.
108
+
109
+ Use:
110
+
111
+ ```bash
112
+ s-gw agent codeguard-plan AGENT
113
+ ```
114
+
115
+ Recommended current paths:
116
+
117
+ | Agent | CodeGuard route | Install surface |
118
+ | --- | --- | --- |
119
+ | Claude Code | Plugin marketplace | `/plugin install codeguard-security@project-codeguard` |
120
+ | Codex | Agent Skill | `./.agents/skills/codeguard` |
121
+ | Cursor | Rule files | `./.cursor/rules` |
122
+ | Windsurf | Rule files | `./.windsurf/rules` |
123
+ | GitHub Copilot CLI / VS Code Copilot | Repository instructions | `./.github/instructions` |
124
+ | Antigravity | Rule files | `./.agents/rules` |
125
+ | OpenCode | Agent Skill | `./.opencode/skills/codeguard` |
126
+ | OpenClaw | Agent Skill | `./.openclaw/skills/codeguard` |
127
+ | Hermes | Agent Skill | `./.hermes/skills/codeguard` |
128
+
129
+ Codex deserves extra care: CodeGuard's current package uses project-local `.agents/skills/codeguard`. Project-local `.codex/skills` was used by older packages and should be treated as stale for CodeGuard. User-level Codex skills under `~/.codex/skills` are still a separate Codex home-level surface.
130
+
131
+ ## Agent Enforcement Surfaces
132
+
133
+ MCP registration and an agent's enforcement surface are separate concerns. MCP gives an agent tools; hooks, plugins, and policies determine whether a product can observe prompts, tool calls, command execution, or post-tool output. The table records those distinctions without claiming that s-gw installs every hook.
134
+
135
+ | Agent | DefenseClaw hook style | Config surface | Native ask? | s-gw implication |
136
+ | --- | --- | --- | --- | --- |
137
+ | OpenClaw, ZeptoClaw | Proxy connectors | App-owned proxy/config files | Proxy-owned | Use local MCP registration; remote proxying is outside s-gw's credential boundary. |
138
+ | Claude Code | Native hooks | `~/.claude/settings.json` | PreToolUse | Keep warning users that MCP config is `.mcp.json`/`~/.claude.json`, not `settings.json`. |
139
+ | Codex | Native hooks plus OTel/notify | `~/.codex/config.toml` | Permission flow | Current support covers local MCP registration and s-gw approval for credential-backed actions. |
140
+ | Cursor | Native hooks | `~/.cursor/hooks.json` | `beforeShellExecution`, `beforeMCPExecution` | Hook installation is not currently managed by s-gw. |
141
+ | Windsurf | Cascade hooks | `~/.codeium/windsurf/hooks.json` | No | s-gw approval remains the supported credential control path. |
142
+ | Gemini CLI | Native hooks | `~/.gemini/settings.json` | No | MCP and hooks share a file, so installer must preserve unrelated settings carefully. |
143
+ | GitHub Copilot CLI | Hook JSON | `~/.copilot/hooks/defenseclaw.json`, `./.github/hooks/defenseclaw.json` | `preToolUse` | s-gw currently documents MCP setup rather than installing hook files. |
144
+ | OpenHands | Hook JSON | `~/.openhands/hooks.json`, optional `./.openhands/hooks.json` | No | Global hook install by default; workspace install only on explicit request. |
145
+ | Antigravity | Native lifecycle hooks | `~/.gemini/config/hooks.json` | `decision=ask` on pre-action events | Write only the canonical global hook file to avoid duplicate firings. |
146
+ | OpenCode | Auto-loaded JS plugin | `~/.config/opencode/plugins/defenseclaw.js` | No | Hook installer would be a managed plugin, not a JSON hook entry. |
147
+ | OmniGent | Custom Python policy API | `$OMNIGENT_CONFIG_HOME/config.yaml`, `~/.omnigent/config.yaml` | Pre-action policy phases | Needs a real s-gw policy bridge before public compatibility claims. |
148
+
149
+ ## Integration Boundary
150
+
151
+ DefenseClaw connectors often patch hooks, proxy routes, telemetry, and subprocess shims. s-gw intentionally starts narrower:
152
+
153
+ - register a local stdio MCP server;
154
+ - launch CLI agents with credential-like environment values replaced by SGW handles;
155
+ - expose tokenization and handle metadata;
156
+ - require local user approval before secret-backed execution;
157
+ - inject secrets only into local child processes;
158
+ - sanitize command output before returning it to the agent.
159
+
160
+ s-gw does not automatically modify agent configuration yet. Use `s-gw agent mcp-snippet` to render a configuration, review it, and apply it through the agent's supported configuration path.
@@ -0,0 +1,72 @@
1
+ # Architecture
2
+
3
+ s-gw is a local credential broker. Its storage, approval, execution, and user interfaces run on the same machine as the coding agent.
4
+
5
+ ```mermaid
6
+ flowchart LR
7
+ Agent["Coding agent or MCP client"] --> MCP["s-gw MCP server"]
8
+ CLI["s-gw CLI"] --> Store["Encrypted ledger"]
9
+ MCP --> Store
10
+ Store --> Review["Native app / menu helper / local console"]
11
+ Review --> Store
12
+ Store --> Broker["Approval broker"]
13
+ Keychain[("Keychain / Credential Manager") ] --> Broker
14
+ OnePassword[("Optional 1Password source") ] --> Broker
15
+ Broker --> Runner["Rust execution core"]
16
+ Runner --> Sanitizer["Bounded output sanitizer"]
17
+ Sanitizer --> MCP
18
+ ```
19
+
20
+ ## Components
21
+
22
+ ### CLI And MCP Server
23
+
24
+ The TypeScript CLI owns setup, handle enrollment, request creation, approval orchestration, diagnostics, and integration output. The stdio MCP server exposes the narrower agent-facing surface: scan data, list or describe non-secret handles, create requests, and execute requests that have already been approved.
25
+
26
+ ### Encrypted Ledger
27
+
28
+ The ledger stores handle metadata, encrypted local values or credential-store pointers, request manifests, reusable grants, policy rules, and audit events. Its unlock material comes from the operating system credential store during normal desktop use. `SGW_MASTER_PASSPHRASE` exists for tests and controlled automation.
29
+
30
+ ### Credential Backends
31
+
32
+ - **macOS Keychain:** the preferred backend on macOS.
33
+ - **Windows Credential Manager:** the preview backend on Windows.
34
+ - **Encrypted local value:** compatibility and test path.
35
+ - **1Password reference:** optional source backed by the local `op` CLI, with an encrypted local cache for a bounded reusable approval.
36
+
37
+ Backends resolve values only inside the local approved execution path. Agent-facing APIs return handles and metadata.
38
+
39
+ ### Approval Surfaces
40
+
41
+ The native macOS app, standalone menu helper, local web console, and CLI read and update the same request records. A request includes the handle, action kind, command or SSH destination, environment binding, working directory, agent identity, reason, and state.
42
+
43
+ Reusable approvals are matched against their stored scope. They are not a general unlock for the credential store.
44
+
45
+ ### Local Runner
46
+
47
+ The approval broker claims the request, validates the handle policy again, and resolves credential values locally. Approved environment commands are then sent to the compiled `sgw-core` runner over stdin using protocol version 1. Credential values never appear in runner arguments or its inherited environment.
48
+
49
+ The Rust runner clears the child environment, restores a small allowlist of ordinary process variables, injects only approved credential bindings, enforces the timeout, captures bounded stdout and stderr, replaces known values with handles, and returns a proof-bound result. The broker rejects malformed responses, raw credential output, or an invalid proof before updating the request record.
50
+
51
+ Owned SSH sessions use the TypeScript execution path. Platform-native packages include the Rust runner and select it automatically for approved environment commands. A Windows preview assembled on macOS uses the TypeScript compatibility path because it cannot contain a Windows-native runner; Windows builds include `sgw-core.exe`. `SGW_EXECUTION_ENGINE=rust` can require the compiled runner, while `SGW_EXECUTION_ENGINE=typescript` selects the compatibility path explicitly.
52
+
53
+ ### User Interfaces
54
+
55
+ The React console is served on loopback and requires a per-session token for state-changing requests. The native macOS app and menu helper use the installed CLI and local store. The Windows preview client hosts the loopback console in a browser app window and provides a tray helper for queue actions.
56
+
57
+ ## Process Boundaries
58
+
59
+ - MCP uses stdio and does not expose a remote credential API.
60
+ - The browser console binds to `127.0.0.1` by default.
61
+ - New secrets and unlock values are accepted over stdin, not command-line arguments.
62
+ - Approved environment-command credentials cross from the broker to `sgw-core` over a private stdin pipe, not process arguments or inherited environment variables.
63
+ - Secret-backed commands run as child processes of the local s-gw process under the current operating system user.
64
+ - s-gw-owned SSH sessions use private control sockets under the local s-gw home.
65
+
66
+ See the [threat model](threat-model.md) for what these boundaries do and do not protect.
67
+
68
+ ## Deployment Models
69
+
70
+ The open-source product is a complete standalone endpoint: Rust core, local storage, approvals, policies, agent integrations, audit history, and desktop clients. Independent local installations do not require a control plane.
71
+
72
+ Team deployments can add a control plane for endpoint fleets, shared policy, delegated approvals, identity, compliance retention, and enterprise vault references. The intended boundary exchanges handle metadata, signed policy, approval state, and sanitized events. Raw credentials remain in the endpoint credential store or an enterprise vault.