rds_ssm_connect 2.0.0 → 2.0.2

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.
@@ -77,6 +77,9 @@ jobs:
77
77
  npx @yao-pkg/pkg src-tauri/binaries/gui-adapter-bundle.cjs --target ${{ matrix.sidecar_target }} -o src-tauri/binaries/gui-adapter-${{ matrix.sidecar_triple }}
78
78
  rm src-tauri/binaries/gui-adapter-bundle.cjs
79
79
 
80
+ - name: Download session-manager-plugin
81
+ run: node scripts/download-ssm-plugin.js --triple ${{ matrix.sidecar_triple }}
82
+
80
83
  - name: Build Tauri app
81
84
  uses: tauri-apps/tauri-action@v0
82
85
  env:
@@ -64,15 +64,10 @@ jobs:
64
64
  homepage "https://github.com/yarka-guru/connection_app"
65
65
 
66
66
  depends_on macos: ">= :monterey"
67
- depends_on formula: "aws-vault"
68
- depends_on formula: "awscli"
69
67
 
70
68
  app "RDS SSM Connect.app"
71
69
 
72
70
  caveats <<~EOS
73
- You also need the AWS Session Manager Plugin:
74
- brew install --cask session-manager-plugin
75
-
76
71
  Ensure your AWS profiles are configured in ~/.aws/config
77
72
  EOS
78
73
 
@@ -104,9 +99,6 @@ jobs:
104
99
  end
105
100
  end
106
101
 
107
- depends_on "awscli"
108
- depends_on "aws-vault"
109
-
110
102
  def install
111
103
  system "ar", "x", cached_download
112
104
  mkdir_p "extract"
@@ -118,9 +110,6 @@ jobs:
118
110
 
119
111
  def caveats
120
112
  <<~EOS
121
- You also need the AWS Session Manager Plugin:
122
- https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager-working-with-install-plugin.html
123
-
124
113
  Ensure your AWS profiles are configured in ~/.aws/config
125
114
 
126
115
  On macOS, install the desktop app instead:
package/CLAUDE.md CHANGED
@@ -4,34 +4,67 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
4
4
 
5
5
  ## Project Overview
6
6
 
7
- **rds_ssm_connect** is a Node.js CLI tool that enables secure connections to AWS RDS databases through AWS Systems Manager (SSM) port forwarding via bastion hosts. The tool reads AWS profiles from the user's config, retrieves database credentials from AWS Secrets Manager, and automatically sets up port forwarding through a bastion instance.
7
+ **rds_ssm_connect** is a Node.js CLI tool and Tauri desktop app that enables secure connections to AWS RDS databases through AWS Systems Manager (SSM) port forwarding via bastion hosts. Projects are user-configurable — the tool reads AWS profiles from `~/.aws/config`, loads project definitions from `~/.rds-ssm-connect/projects.json`, retrieves database credentials from AWS Secrets Manager, and sets up port forwarding through a bastion instance.
8
8
 
9
9
  ## Key Architecture
10
10
 
11
11
  ### Entry Point
12
12
  - `connect.js` - Main executable (shebang: `#!/usr/bin/env node`)
13
13
  - Reads AWS profiles from `~/.aws/config`
14
- - Uses `inquirer` for interactive environment selection
15
- - Executes AWS commands via `aws-vault` wrapper
16
- - Establishes SSM port forwarding session
14
+ - Loads project configs from `~/.rds-ssm-connect/projects.json` via `configLoader.js`
15
+ - Uses `inquirer` for interactive project/environment selection
16
+ - Includes first-run wizard when no projects are configured
17
+ - Uses AWS SDK v3 directly (STS, EC2, RDS, SSM, Secrets Manager, SSO OIDC)
18
+ - Ensures SSO session is valid before connecting (via `src/sso-login.js`)
19
+ - Establishes SSM port forwarding session with keepalive and auto-reconnect
17
20
 
18
21
  ### Configuration
19
- - `envPortMapping.js` - Multi-project configuration
20
- - `PROJECT_CONFIGS` - Object containing per-project settings:
21
- - `tln`: TLN/EMR project (us-east-2, Aurora clusters, `rds!cluster` secrets)
22
- - `covered`: Covered Healthcare project (us-west-1, RDS instances, `rds!db` secrets)
23
- - Each project defines: region, database, secretPrefix, rdsType, rdsPattern, profileFilter, envPortMapping
24
- - Legacy exports maintained for backward compatibility
22
+ - `configLoader.js` - Project config CRUD for `~/.rds-ssm-connect/projects.json`
23
+ - `loadProjectConfigs()` / `saveProjectConfig()` / `deleteProjectConfig()`
24
+ - `validateProjectConfig()` - validates required fields, region format, port format, shell-safe patterns
25
+ - Each project defines: name, region, database, secretPrefix, rdsType, engine, rdsPattern, profileFilter, envPortMapping, defaultPort
26
+
27
+ ### GUI Adapter
28
+ - `gui-adapter.js` - JSON stdin/stdout IPC bridge for Tauri sidecar
29
+ - Commands: `list-projects`, `list-profiles`, `connect`, `disconnect`, `disconnect-all`, `status`, `list-project-configs`, `save-project-config`, `delete-project-config`, `sso-login`, `ping`
30
+ - Manages multiple simultaneous connections with strict port availability checks
31
+ - SSO pre-flight validation before connecting
32
+
33
+ ### Backend Modules (src/)
34
+ - `aws-clients.js` - AWS SDK client factory (STS, EC2, RDS, SSM, Secrets Manager)
35
+ - `aws-operations.js` - AWS operations (find bastion, get endpoint, get credentials, start session, etc.)
36
+ - `credential-resolver.js` - AWS credential chain resolution (SSO, profiles)
37
+ - `sso-login.js` - AWS SSO OIDC device authorization flow
38
+ - `plugin-resolver.js` - Locates session-manager-plugin binary
39
+
40
+ ### Tauri Desktop App
41
+ - `src-tauri/src/lib.rs` - Tauri commands (connect, disconnect, saved connections CRUD, project config CRUD, AWS profile CRUD, updates)
42
+ - `src-tauri/tauri.conf.json` - App config, plugins, window settings, bundling
43
+ - Session Manager Plugin is bundled as an external binary
44
+
45
+ ### Frontend (src/)
46
+ - `App.svelte` - Main app shell (Svelte 5 with runes)
47
+ - `lib/ConnectionForm.svelte` - Project/environment selector + connect button
48
+ - `lib/ActiveConnections.svelte` - Live connection panels with credentials
49
+ - `lib/SavedConnections.svelte` - Bookmarked connections list
50
+ - `lib/Settings.svelte` - Project management + AWS profile CRUD + raw config editor
51
+ - `lib/SessionStatus.svelte` - Connection status indicator
52
+ - `lib/UpdateBanner.svelte` - In-app update notification
53
+ - `lib/CopyButton.svelte` - Reusable copy-to-clipboard with feedback
54
+ - `lib/ConfirmDialog.svelte` - Reusable confirmation modal
55
+ - `lib/utils.js` - Shared utilities (clipboard, timeout, focus trap)
25
56
 
26
57
  ### Core Flow
27
58
  1. Read AWS profiles from `~/.aws/config`
28
- 2. Prompt user to select project (TLN or Covered)
29
- 3. Filter and prompt for environment (AWS profile) based on project
30
- 4. Query Secrets Manager for RDS credentials (project-specific prefix)
31
- 5. Find running bastion instance (tagged with `Name=*bastion*`)
32
- 6. Get RDS endpoint (cluster or instance based on project config)
33
- 7. Start SSM port forwarding session with correct remote port
34
- 8. Display connection details for database client
59
+ 2. Load project configs from `~/.rds-ssm-connect/projects.json`
60
+ 3. Prompt user to select project (filtered by available profiles)
61
+ 4. Filter and prompt for environment (AWS profile) based on project's `profileFilter`
62
+ 5. Ensure SSO session is valid (OIDC device authorization if needed)
63
+ 6. Query Secrets Manager for RDS credentials (project-specific `secretPrefix`)
64
+ 7. Find running bastion instance (tagged with `Name=*bastion*`)
65
+ 8. Get RDS endpoint (cluster or instance based on project's `rdsType`)
66
+ 9. Start SSM port forwarding session with correct local/remote ports
67
+ 10. Display connection details for database client
35
68
 
36
69
  ## Development Commands
37
70
 
@@ -45,16 +78,24 @@ npm install
45
78
  npm test
46
79
  ```
47
80
  Tests cover:
48
- - AWS config parsing
49
- - Port mapping logic
50
- - Credentials validation
51
- - Retry configuration validation
81
+ - Project config loading, validation, and CRUD (`configLoader.test.js`)
82
+ - AWS operations (`aws-operations.test.js`)
83
+ - Credential resolution (`credential-resolver.test.js`)
84
+ - SSO login flow (`sso-login.test.js`)
85
+ - Plugin resolution (`plugin-resolver.test.js`)
86
+ - Port mapping and config parsing (`connect.test.js`)
52
87
 
53
88
  ### Local Testing
54
89
  ```bash
55
90
  node connect.js
56
91
  ```
57
92
 
93
+ ### Desktop App Development
94
+ ```bash
95
+ npm run dev:gui # Tauri dev mode (full app)
96
+ npm run build:gui # Build Tauri desktop app
97
+ ```
98
+
58
99
  ### Global Installation (for testing as installed package)
59
100
  ```bash
60
101
  npm install -g .
@@ -62,38 +103,34 @@ rds_ssm_connect
62
103
  ```
63
104
 
64
105
  ### Publishing
65
- Package is published to npm via GitHub Actions workflow (`.github/workflows/npm-publish.yml`) when a release is created.
106
+ - **npm**: Published via GitHub Actions workflow (`.github/workflows/npm-publish.yml`) when a release is created
107
+ - **Desktop**: Multi-platform builds (macOS ARM64/x64, Linux ARM64/x64, Windows x64) via `tauri-action` on git tags
66
108
 
67
109
  ## Prerequisites for Running
68
110
 
69
- - `aws-vault` - Required for AWS credential management
70
- - AWS CLI - Required for AWS API calls
71
111
  - Node.js (ES modules enabled via `"type": "module"` in package.json)
72
112
  - Properly configured `~/.aws/config` with named profiles
113
+ - Session Manager Plugin (bundled in desktop app; CLI requires separate install)
114
+
115
+ Note: aws-vault and AWS CLI are **not required** — the app uses AWS SDK v3 natively for all API calls and credential resolution.
73
116
 
74
117
  ## AWS Resource Naming Conventions
75
118
 
76
- The application relies on specific AWS resource naming patterns (per project):
119
+ The application relies on AWS resource naming patterns defined per project in `~/.rds-ssm-connect/projects.json`:
77
120
 
78
- **TLN Project:**
79
- - **Secrets**: Must start with `rds!cluster`
121
+ - **Secrets**: Must start with the project's `secretPrefix` (e.g., `rds!cluster`, `rds!db`)
80
122
  - **Bastion instances**: Must be tagged with `Name=*bastion*` and in `running` state
81
- - **RDS clusters**: DBClusterIdentifier must end with `-rds-aurora` and be in `available` state
82
- - **Region**: us-east-2
83
-
84
- **Covered Project:**
85
- - **Secrets**: Must start with `rds!db`
86
- - **Bastion instances**: Must be tagged with `Name=*bastion*` and in `running` state
87
- - **RDS instances**: DBInstanceIdentifier must contain `covered-db` and be in `available` state
88
- - **Region**: us-west-1
89
- - **AWS Profiles**: Must start with `covered` (e.g., `covered`, `covered-staging`)
123
+ - **RDS clusters**: DBClusterIdentifier must match the project's `rdsPattern` and be `available` (when `rdsType` is `cluster`)
124
+ - **RDS instances**: DBInstanceIdentifier must match the project's `rdsPattern` and be `available` (when `rdsType` is `instance`)
90
125
 
91
126
  ## Important Notes
92
127
 
93
- - Port assignment is based on project-specific port mappings
128
+ - Projects are user-configurable via `~/.rds-ssm-connect/projects.json` (no hardcoded defaults)
129
+ - Port assignment is based on project-specific `envPortMapping` with `defaultPort` fallback
94
130
  - The tool keeps the SSM session running until Ctrl+C is pressed
95
- - Each project has its own default port if no environment suffix matches
96
- - AWS region is determined by the selected project
131
+ - AWS region is determined by the selected project's `region` field
132
+ - SSO sessions are validated before connecting; browser opens automatically if needed
133
+ - Desktop app bundles Session Manager Plugin as an external binary
97
134
 
98
135
  ## Error Handling & Recovery
99
136
 
@@ -106,12 +143,26 @@ The application automatically handles the race condition where a bastion instanc
106
143
  4. Verifies SSM agent is online using `describe-instance-information`
107
144
  5. Retries port forwarding with new instance (max 2 retries)
108
145
 
146
+ ### Auto-Reconnect
147
+ When an established session drops unexpectedly (idle timeout, network issue):
148
+
149
+ 1. Verifies AWS credentials are still valid (avoids opening SSO tabs when user is away)
150
+ 2. Re-discovers infrastructure (bastion may have been replaced by ASG)
151
+ 3. Reconnects on the same local port (up to 3 attempts with 3s delay)
152
+
153
+ ### Keepalive
154
+ Periodic TCP pings every 4 minutes prevent SSM from timing out idle connections.
155
+
109
156
  ### Configuration Constants
110
- All retry/timeout values are configurable in `RETRY_CONFIG`:
157
+ All retry/timeout values are configurable in `RETRY_CONFIG` (`connect.js`):
111
158
  - `BASTION_WAIT_MAX_RETRIES`: 20 (time to wait for new instance)
112
159
  - `BASTION_WAIT_RETRY_DELAY_MS`: 15000 (delay between instance checks)
113
160
  - `PORT_FORWARDING_MAX_RETRIES`: 2 (connection retry attempts)
114
161
  - `SSM_AGENT_READY_WAIT_MS`: 10000 (stabilization time after agent online)
162
+ - `KEEPALIVE_INTERVAL_MS`: 240000 (TCP ping interval, 4 minutes)
163
+ - `AUTO_RECONNECT_MAX_RETRIES`: 3 (auto-reconnect attempts)
164
+ - `AUTO_RECONNECT_DELAY_MS`: 3000 (delay between reconnect attempts)
165
+ - `CREDENTIAL_CHECK_TIMEOUT_MS`: 60000 (credential validation timeout)
115
166
 
116
167
  ### Process Cleanup
117
- The application registers handlers for `SIGINT`, `SIGTERM`, and `exit` events to properly clean up child processes (SSM sessions) to prevent zombie processes.
168
+ The application registers handlers for `SIGINT`, `SIGTERM`, and `exit` events to properly clean up child processes (SSM sessions) using a three-strategy kill approach (process group, individual SIGTERM, SIGKILL) to prevent zombie processes.
package/README.md CHANGED
@@ -4,21 +4,22 @@ Secure database tunneling to AWS RDS through SSM port forwarding via bastion hos
4
4
 
5
5
  ## Features
6
6
 
7
- - **Multi-project support** — TLN (Aurora clusters, us-east-2) and Covered (RDS instances, us-west-1)
8
- - **Multiple simultaneous connections** with automatic port assignment
7
+ - **User-configurable projects** — define any number of RDS projects (Aurora clusters or RDS instances, PostgreSQL or MySQL)
8
+ - **Multiple simultaneous connections** with strict port availability checks
9
9
  - **Saved connections** — bookmark frequently used profiles with one-click connect
10
- - **Auto-reconnect** — handles `TargetNotConnected` errors by cycling bastion instances via ASG
10
+ - **Auto-reconnect** — transparently reconnects on the same port if the session drops unexpectedly
11
+ - **TargetNotConnected recovery** — cycles bastion instances via ASG when the SSM agent is disconnected
12
+ - **SSO support** — handles AWS SSO (OIDC device authorization) with automatic browser launch
13
+ - **Keepalive** — periodic TCP pings prevent SSM idle timeout
11
14
  - **In-app updates** — checks GitHub releases, downloads and installs signed updates
12
- - **Prerequisites validation** — detects missing `aws-vault` and AWS CLI on launch
13
15
  - **Keyboard shortcuts** — `Cmd/Ctrl + ,` for settings
14
16
  - **Accessible** — ARIA labels, focus trapping, keyboard navigation, screen reader support
15
17
 
16
18
  ## Prerequisites
17
19
 
18
- - [aws-vault](https://github.com/99designs/aws-vault) — AWS credential management
19
- - [AWS CLI](https://aws.amazon.com/cli/) — AWS API access
20
20
  - [Node.js](https://nodejs.org/) 22+ (CLI only)
21
21
  - AWS profiles configured in `~/.aws/config`
22
+ - [Session Manager Plugin](https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager-working-with-install-plugin.html) (bundled with desktop app; CLI requires separate install)
22
23
 
23
24
  ## Installation
24
25
 
@@ -29,12 +30,6 @@ brew tap yarka-guru/tap
29
30
  brew install --cask rds-ssm-connect
30
31
  ```
31
32
 
32
- This installs the desktop app along with `aws-vault` and `awscli` dependencies. You also need the [Session Manager Plugin](https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager-working-with-install-plugin.html):
33
-
34
- ```bash
35
- brew install --cask session-manager-plugin
36
- ```
37
-
38
33
  Or download the `.dmg` directly from [GitHub Releases](https://github.com/yarka-guru/connection_app/releases).
39
34
 
40
35
  ### Linux
@@ -47,15 +42,10 @@ Or download the `.dmg` directly from [GitHub Releases](https://github.com/yarka-
47
42
  echo 'eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)"' >> ~/.bashrc
48
43
  eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)"
49
44
 
50
- # Install the app (includes aws-vault and awscli as dependencies)
45
+ # Install the app
51
46
  brew tap yarka-guru/tap
52
47
  brew install yarka-guru/tap/rds-ssm-connect
53
48
 
54
- # Install Session Manager Plugin (ARM64)
55
- curl "https://s3.amazonaws.com/session-manager-downloads/plugin/latest/ubuntu_arm64/session-manager-plugin.deb" -o session-manager-plugin.deb
56
- sudo dpkg -i session-manager-plugin.deb
57
- # For x86_64, replace ubuntu_arm64 with ubuntu_64bit
58
-
59
49
  # Make brew tools visible to the desktop app
60
50
  echo 'export PATH="/home/linuxbrew/.linuxbrew/bin:$PATH"' | sudo tee /etc/profile.d/linuxbrew.sh
61
51
  # Log out and back in for this to take effect
@@ -64,41 +54,20 @@ echo 'export PATH="/home/linuxbrew/.linuxbrew/bin:$PATH"' | sudo tee /etc/profil
64
54
  #### Option B: Direct .deb install
65
55
 
66
56
  ```bash
67
- # 1. Download and install the app
68
- # ARM64:
69
- wget https://github.com/yarka-guru/connection_app/releases/latest/download/RDS.SSM.Connect_1.7.5_arm64.deb
70
- sudo dpkg -i RDS.SSM.Connect_1.7.5_arm64.deb
71
- # x86_64:
72
- # wget https://github.com/yarka-guru/connection_app/releases/latest/download/RDS.SSM.Connect_1.7.5_amd64.deb
73
- # sudo dpkg -i RDS.SSM.Connect_1.7.5_amd64.deb
74
-
75
- # 2. Install aws-vault
76
- # ARM64:
77
- wget https://github.com/99designs/aws-vault/releases/latest/download/aws-vault-linux-arm64 -O aws-vault
78
- # x86_64:
79
- # wget https://github.com/99designs/aws-vault/releases/latest/download/aws-vault-linux-amd64 -O aws-vault
80
- chmod +x aws-vault && sudo mv aws-vault /usr/local/bin/
81
-
82
- # 3. Install AWS CLI
83
- # ARM64:
84
- curl "https://awscli.amazonaws.com/awscli-exe-linux-aarch64.zip" -o awscliv2.zip
85
- # x86_64:
86
- # curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o awscliv2.zip
87
- unzip awscliv2.zip && sudo ./aws/install
88
-
89
- # 4. Install Session Manager Plugin
57
+ # Download and install the app (check GitHub Releases for the latest version)
90
58
  # ARM64:
91
- curl "https://s3.amazonaws.com/session-manager-downloads/plugin/latest/ubuntu_arm64/session-manager-plugin.deb" -o session-manager-plugin.deb
59
+ wget https://github.com/yarka-guru/connection_app/releases/latest/download/RDS.SSM.Connect_2.0.2_arm64.deb
60
+ sudo dpkg -i RDS.SSM.Connect_2.0.2_arm64.deb
92
61
  # x86_64:
93
- # curl "https://s3.amazonaws.com/session-manager-downloads/plugin/latest/ubuntu_64bit/session-manager-plugin.deb" -o session-manager-plugin.deb
94
- sudo dpkg -i session-manager-plugin.deb
62
+ # wget https://github.com/yarka-guru/connection_app/releases/latest/download/RDS.SSM.Connect_2.0.2_amd64.deb
63
+ # sudo dpkg -i RDS.SSM.Connect_2.0.2_amd64.deb
95
64
  ```
96
65
 
97
66
  ### Windows
98
67
 
99
68
  Download the `.msi` or `.exe` installer from [GitHub Releases](https://github.com/yarka-guru/connection_app/releases).
100
69
 
101
- Prerequisites must be installed separately: [aws-vault](https://github.com/99designs/aws-vault), [AWS CLI](https://aws.amazon.com/cli/), [Session Manager Plugin](https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager-working-with-install-plugin.html).
70
+ No additional prerequisites the app uses AWS SDK v3 natively.
102
71
 
103
72
  ### CLI (all platforms)
104
73
 
@@ -106,55 +75,100 @@ Prerequisites must be installed separately: [aws-vault](https://github.com/99des
106
75
  npm install -g rds_ssm_connect
107
76
  ```
108
77
 
78
+ The CLI requires the [Session Manager Plugin](https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager-working-with-install-plugin.html) to be installed separately.
79
+
109
80
  ## Usage
110
81
 
111
82
  ### Desktop App
112
83
 
113
84
  Launch the app, select a project and environment, then click **Connect**. Connection credentials are displayed inline with one-click copy buttons. Save connections for quick access later.
114
85
 
86
+ Manage projects and AWS profiles in **Settings** (`Cmd/Ctrl + ,`).
87
+
115
88
  ### CLI
116
89
 
117
90
  ```bash
118
91
  rds_ssm_connect
119
92
  ```
120
93
 
121
- 1. Select a project (TLN or Covered)
122
- 2. Select an environment (AWS profile)
123
- 3. The tool retrieves credentials from Secrets Manager, finds a bastion instance, and starts SSM port forwarding
124
- 4. Use the displayed connection string with your database client (`psql`, pgAdmin, DBeaver, etc.)
94
+ 1. On first run with no projects configured, an interactive wizard walks you through creating one
95
+ 2. Select a project
96
+ 3. Select an environment (AWS profile)
97
+ 4. SSO session is validated automatically (opens browser if needed)
98
+ 5. The tool retrieves credentials from Secrets Manager, finds a bastion instance, and starts SSM port forwarding
99
+ 6. Use the displayed connection details with your database client (`psql`, `mysql`, pgAdmin, DBeaver, etc.)
125
100
 
126
101
  The tunnel stays open until you press `Ctrl+C`.
127
102
 
128
103
  ## How It Works
129
104
 
130
105
  1. Reads AWS profiles from `~/.aws/config`
131
- 2. Filters profiles based on the selected project
132
- 3. Queries AWS Secrets Manager for RDS credentials (project-specific prefix)
133
- 4. Finds a running bastion instance (tagged `Name=*bastion*`)
134
- 5. Gets the RDS endpoint (cluster or instance depending on project)
135
- 6. Starts an SSM port forwarding session with the correct local port
136
- 7. Displays connection details (host, port, username, password, database)
106
+ 2. Loads project configurations from `~/.rds-ssm-connect/projects.json`
107
+ 3. Filters profiles based on the selected project's `profileFilter`
108
+ 4. Ensures AWS SSO session is valid (OIDC device authorization if needed)
109
+ 5. Queries AWS Secrets Manager for RDS credentials (project-specific `secretPrefix`)
110
+ 6. Finds a running bastion instance (tagged `Name=*bastion*`)
111
+ 7. Gets the RDS endpoint (cluster or instance depending on project `rdsType`)
112
+ 8. Starts an SSM port forwarding session with the correct local/remote ports
113
+ 9. Displays connection details (host, port, username, password, database)
137
114
 
138
115
  ### Error Recovery
139
116
 
140
- When a bastion instance appears running but SSM agent is disconnected (`TargetNotConnected`, exit code 254):
117
+ **TargetNotConnected** — when a bastion instance appears running but SSM agent is disconnected (exit code 254):
141
118
 
142
119
  1. Terminates the disconnected instance
143
120
  2. Waits for ASG to launch a replacement (up to 20 retries, 15s intervals)
144
121
  3. Verifies the SSM agent is online
145
122
  4. Retries port forwarding (up to 2 attempts)
146
123
 
124
+ **Auto-reconnect** — when an established session drops unexpectedly (idle timeout, network issue):
125
+
126
+ 1. Verifies AWS credentials are still valid (avoids opening SSO tabs when user is away)
127
+ 2. Re-discovers infrastructure (bastion may have been replaced by ASG)
128
+ 3. Reconnects on the same local port (up to 3 attempts with 3s delay)
129
+
130
+ **Keepalive** — periodic TCP pings every 4 minutes prevent SSM from timing out idle connections.
131
+
147
132
  ## Project Configuration
148
133
 
149
- | | TLN (EMR) | Covered Healthcare |
150
- |---|---|---|
151
- | Region | us-east-2 | us-west-1 |
152
- | Database | emr | covered_db |
153
- | RDS type | Aurora cluster | RDS instance |
154
- | Secret prefix | `rds!cluster` | `rds!db` |
155
- | Port range | 5432–5452 | 5460–5461 |
134
+ Projects are stored in `~/.rds-ssm-connect/projects.json` and can be managed through the desktop app's Settings UI or the CLI's first-run wizard.
135
+
136
+ Each project defines:
156
137
 
157
- Port assignments are based on environment suffix mappings defined in `envPortMapping.js`.
138
+ | Field | Description | Example |
139
+ |---|---|---|
140
+ | `name` | Display name | `"My Project"` |
141
+ | `region` | AWS region | `"us-east-2"` |
142
+ | `database` | Database name | `"mydb"` |
143
+ | `secretPrefix` | Secrets Manager prefix | `"rds!cluster"` |
144
+ | `rdsType` | `"cluster"` or `"instance"` | `"cluster"` |
145
+ | `engine` | `"postgres"` or `"mysql"` | `"postgres"` |
146
+ | `rdsPattern` | RDS identifier pattern | `"my-app-rds-aurora"` |
147
+ | `profileFilter` | AWS profile prefix filter (optional) | `"my-app"` |
148
+ | `envPortMapping` | Environment suffix to local port mapping | `{"-staging": "5433"}` |
149
+ | `defaultPort` | Fallback local port | `"5432"` |
150
+
151
+ Example `projects.json`:
152
+
153
+ ```json
154
+ {
155
+ "my-project": {
156
+ "name": "My Project",
157
+ "region": "us-east-2",
158
+ "database": "mydb",
159
+ "secretPrefix": "rds!cluster",
160
+ "rdsType": "cluster",
161
+ "engine": "postgres",
162
+ "rdsPattern": "my-app-rds-aurora",
163
+ "profileFilter": null,
164
+ "envPortMapping": {
165
+ "-prod": "5432",
166
+ "-staging": "5433"
167
+ },
168
+ "defaultPort": "5432"
169
+ }
170
+ }
171
+ ```
158
172
 
159
173
  ## Development
160
174
 
@@ -177,11 +191,18 @@ npm run build:gui # Build Tauri desktop app
177
191
  ### Architecture
178
192
 
179
193
  ```
180
- connect.js CLI entry point (shebang, runs standalone)
194
+ connect.js CLI entry point + core connection logic
181
195
  gui-adapter.js IPC bridge — JSON stdin/stdout protocol for Tauri sidecar
182
- envPortMapping.js Multi-project configuration (regions, ports, patterns)
196
+ configLoader.js Project config CRUD (~/.rds-ssm-connect/projects.json)
197
+ src/
198
+ aws-clients.js AWS SDK client factory (STS, EC2, RDS, SSM, Secrets Manager)
199
+ aws-operations.js AWS operations (find bastion, get endpoint, get credentials, etc.)
200
+ credential-resolver.js AWS credential chain resolution (SSO, profiles)
201
+ sso-login.js AWS SSO OIDC device authorization flow
202
+ plugin-resolver.js Locates session-manager-plugin binary
183
203
  src-tauri/
184
- src/lib.rs Tauri commands (connect, disconnect, save, update, etc.)
204
+ src/lib.rs Tauri commands (connect, disconnect, saved connections, project
205
+ config CRUD, AWS profile CRUD, updates, prerequisites check)
185
206
  tauri.conf.json App config, plugins, window settings, bundling
186
207
  src/
187
208
  App.svelte Main app shell (Svelte 5 with runes)
@@ -193,8 +214,7 @@ src/
193
214
  SavedConnections.svelte Bookmarked connections list
194
215
  ActiveConnections.svelte Live connection panels with credentials
195
216
  SessionStatus.svelte Connection status indicator
196
- Settings.svelte AWS profile management (CRUD + raw config editor)
197
- PrerequisitesCheck.svelte Missing dependency warnings
217
+ Settings.svelte Project management + AWS profile CRUD + raw config editor
198
218
  UpdateBanner.svelte In-app update notification
199
219
  ```
200
220
 
@@ -203,7 +223,7 @@ src/
203
223
  - **Frontend**: Svelte 5 (runes), Vite
204
224
  - **Desktop**: Tauri v2 (Rust)
205
225
  - **Backend**: Node.js sidecar bundled with esbuild + pkg
206
- - **AWS SDK**: v3 (EC2, RDS, SSM, Secrets Manager)
226
+ - **AWS SDK**: v3 (STS, EC2, RDS, SSM, Secrets Manager, SSO OIDC)
207
227
  - **Linter**: Biome
208
228
 
209
229
  ## Publishing
package/connect.js CHANGED
@@ -14,9 +14,10 @@ import {
14
14
  import { createAwsClients, destroyAwsClients } from './src/aws-clients.js'
15
15
  import * as ops from './src/aws-operations.js'
16
16
  import { findPluginBinary, spawnPlugin } from './src/plugin-resolver.js'
17
+ import { ensureSsoSession } from './src/sso-login.js'
17
18
 
18
19
  // Package info for version checking
19
- const packageJson = { name: 'rds_ssm_connect', version: '1.8.3' }
20
+ const packageJson = { name: 'rds_ssm_connect', version: '2.0.2' }
20
21
 
21
22
  // Event emitter for IPC communication
22
23
  const ipcEmitter = new EventEmitter()
@@ -962,6 +963,20 @@ async function main() {
962
963
  allEnvSuffixes.find((suffix) => ENV === suffix)
963
964
  const portNumber = envPortMapping[matchedSuffix] || defaultPort
964
965
 
966
+ // Ensure SSO session is valid (if SSO profile)
967
+ await ensureSsoSession(ENV, {
968
+ onEvent: (_event, data) => {
969
+ if (data?.message) console.log(`\u23F3 ${data.message}`)
970
+ },
971
+ onOpenUrl: (url) => {
972
+ console.log(`\n\uD83C\uDF10 Open this URL in your browser to authorize:\n ${url}\n`)
973
+ // Try to open browser automatically
974
+ const { platform } = process
975
+ const cmd = platform === 'darwin' ? 'open' : platform === 'win32' ? 'start' : 'xdg-open'
976
+ try { spawnSync(cmd, [url], { stdio: 'ignore' }) } catch {}
977
+ },
978
+ })
979
+
965
980
  // Create SDK clients ONCE for this CLI session
966
981
  const clients = createAwsClients(ENV, region)
967
982
 
@@ -1021,9 +1036,10 @@ async function main() {
1021
1036
  } finally {
1022
1037
  destroyAwsClients(clients)
1023
1038
  }
1024
- } catch (_error) {
1039
+ } catch (error) {
1040
+ console.error(`\n\u274C ${error.message || error}`)
1025
1041
  setImmediate(() => {
1026
- throw new Error('Forcing exit due to unhandled error')
1042
+ process.exit(1)
1027
1043
  })
1028
1044
  }
1029
1045
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rds_ssm_connect",
3
- "version": "2.0.0",
3
+ "version": "2.0.2",
4
4
  "type": "module",
5
5
  "repository": {
6
6
  "type": "git",
@@ -14,7 +14,7 @@
14
14
  "@aws-sdk/client-sso-oidc": "^3.1000.0",
15
15
  "@aws-sdk/client-sts": "^3.1000.0",
16
16
  "@aws-sdk/credential-providers": "^3.1000.0",
17
- "fast-xml-parser": "^5.3.6",
17
+ "fast-xml-parser": "^5.3.8",
18
18
  "glob": "^13.0.0",
19
19
  "inquirer": "^13.2.2",
20
20
  "rimraf": "^6.1.2"
@@ -35,7 +35,7 @@
35
35
  "vite": "^6.4.1"
36
36
  },
37
37
  "overrides": {
38
- "fast-xml-parser": "^5.3.6"
38
+ "fast-xml-parser": "^5.3.8"
39
39
  },
40
40
  "bin": {
41
41
  "rds_ssm_connect": "./connect.js"
@@ -216,6 +216,29 @@ function extractWindowsZip(archivePath, tmpDir) {
216
216
  }
217
217
  }
218
218
 
219
+ // The AWS Windows zip has nested archives:
220
+ // outer.zip → SessionManagerPlugin.zip → package.zip → session-manager-plugin.exe
221
+ // Extract all nested zips we find.
222
+ function expandZip(zipPath, destDir) {
223
+ mkdirSync(destDir, { recursive: true });
224
+ if (isWindows) {
225
+ execSync(
226
+ `powershell -Command "Expand-Archive -Force -Path '${zipPath}' -DestinationPath '${destDir}'"`,
227
+ { stdio: "pipe" },
228
+ );
229
+ } else {
230
+ execSync(`unzip -o "${zipPath}" -d "${destDir}"`, { stdio: "pipe" });
231
+ }
232
+ }
233
+
234
+ for (const nestedName of ["SessionManagerPlugin.zip", "package.zip"]) {
235
+ const found = findFileRecursive(tmpDir, nestedName);
236
+ if (found) {
237
+ const destDir = join(tmpDir, nestedName.replace(".zip", "-extracted"));
238
+ expandZip(found, destDir);
239
+ }
240
+ }
241
+
219
242
  // Search for the exe — could be in bin/ subdirectory or at root
220
243
  const candidates = [
221
244
  join(tmpDir, "bin", "session-manager-plugin.exe"),
@@ -355,12 +378,25 @@ async function processTarget(target) {
355
378
  async function main() {
356
379
  const args = process.argv.slice(2);
357
380
  const currentPlatformOnly = args.includes("--current-platform-only");
381
+ const tripleIndex = args.indexOf("--triple");
382
+ const tripleArg = tripleIndex !== -1 ? args[tripleIndex + 1] : null;
358
383
 
359
384
  // Ensure output directory exists
360
385
  mkdirSync(BINARIES_DIR, { recursive: true });
361
386
 
362
387
  let targets;
363
- if (currentPlatformOnly) {
388
+ if (tripleArg) {
389
+ targets = TARGETS.filter((t) => t.triple === tripleArg);
390
+ if (targets.length === 0) {
391
+ console.error(`Error: Unknown triple "${tripleArg}".`);
392
+ console.error("Available triples:");
393
+ for (const t of TARGETS) {
394
+ console.error(` - ${t.triple}`);
395
+ }
396
+ process.exit(1);
397
+ }
398
+ console.log(`Downloading for triple: ${tripleArg}\n`);
399
+ } else if (currentPlatformOnly) {
364
400
  targets = getTargetsForCurrentPlatform();
365
401
  if (targets.length === 0) {
366
402
  console.warn(