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: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ It combines a Bubble Tea application, Cobra-based CLI commands, and AWS SDK v2 c
- Drill down into resources with filters, detail views, and action screens
- Open a context-aware keyboard shortcut help screen with `?`
- Show animated loading indicators while async AWS data is being fetched
- Perform operational workflows such as EC2 inventory inspection, SSM sessions, RDS control, Route53 record changes, ECS rollout inspection/exec, IAM access key rotation, and Bedrock API key management
- Perform operational workflows such as EC2 inventory inspection, SSM sessions, RDS control, Route53 record changes, ECS rollout inspection/exec, EKS cluster and node group review, IAM access key rotation, and Bedrock API key management
- Press `i` from the service picker to enter Inspector mode, then run either the Security Inspector workflow for built-in findings or the Checklist Inspector workflow for YAML-driven readiness checks across databases, network resources, DNS, logging, secrets, and baseline posture. Checklist files can be loaded from the in-TUI picker or preloaded with `--checklist <path>`

## Documentation Map
Expand Down Expand Up @@ -226,6 +226,7 @@ Context ordering:
| CloudWatch Logs | Logs Browser |
| ECR | ECR Login Helper |
| ECS | ECS Browser & Exec |
| EKS | Cluster & Node Group Browser |
| S3 | S3 Browser |
| Lambda | Lambda Browser |
| Bedrock | API Key Manager |
Expand Down Expand Up @@ -340,13 +341,14 @@ checks:
| ECR Login | CLI helper: `unic ecr login [--runtime docker|podman] [--copy]` |
| ECS Exec | `r` refresh, `Enter` drill down / exec |
| ECS Rollout / Exec | cluster/service lists support refresh and drill-down, service detail shows deployments/task definition images/events, `Enter` continues into tasks and exec |
| EKS Browser | cluster/node group lists support `/` filter and `r` refresh, cluster view shows version/status/endpoint visibility/ARN summary, node group detail shows desired/min/max scaling plus health issues |
| Inspector Mode | `i` open mode from the service list, `Enter` open the selected workflow, `l` open the checklist file picker |
| Security Inspector | `r` run/rescan, `1`-`5` severity filter, `Enter` finding detail |
| Checklist Inspector | `l` load or switch checklist files, `r` run/rerun the loaded checklist, `Enter` result detail |
| Context Picker | `a` add context, type or `/` filter, `s` setup selected context and quit, `y` copy selected exports and quit, `u` clear shell context and quit with a final confirmation message |
| Lambda | `Enter` invoke, `d` detail, `l` view CloudWatch Logs, `/` filter, `r` refresh |

The service list defaults to favorites first, then alphabetical order. Press `f` to favorite or unfavorite the selected service; favorites are saved under `favorites.services` in `config.yaml` and rendered with a distinct marker/style. The service list supports `/` filtering across service names, feature names, and feature descriptions. Shared list filters use fuzzy matching with inline match highlighting. While filter mode stays active, `↑`/`↓` continue to move through the filtered results without requiring an extra Enter first. Filtering is currently available on the service list, EC2 SSM instances, EC2 inventory instances, IAM users, VPCs, subnets, RDS instances, Route53 zones/records, CloudWatch metrics, CloudWatch log groups/streams, Secrets Manager resources, ECS clusters/services, S3 buckets/objects, Lambda functions, Bedrock API keys, and the context picker.
The service list defaults to favorites first, then alphabetical order. Press `f` to favorite or unfavorite the selected service; favorites are saved under `favorites.services` in `config.yaml` and rendered with a distinct marker/style. The service list supports `/` filtering across service names, feature names, and feature descriptions. Shared list filters use fuzzy matching with inline match highlighting. While filter mode stays active, `↑`/`↓` continue to move through the filtered results without requiring an extra Enter first. Filtering is currently available on the service list, EC2 SSM instances, EC2 inventory instances, IAM users, VPCs, subnets, RDS instances, Route53 zones/records, CloudWatch metrics, CloudWatch log groups/streams, Secrets Manager resources, ECS clusters/services, EKS clusters/node groups, S3 buckets/objects, Lambda functions, Bedrock API keys, and the context picker.

The EC2 Instance Browser lists EC2 instances across available states for the active context and region, separate from the SSM session picker that only lists connectable running instances. The detail screen shows core metadata including instance ID, name tag, state, instance type, AZ, VPC, subnet, private and public IPs, launch time, platform details, IAM profile, and tags.

Expand Down
9 changes: 5 additions & 4 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module unic
go 1.24.2

require (
github.com/aws/aws-sdk-go-v2 v1.41.5
github.com/aws/aws-sdk-go-v2 v1.41.6
github.com/aws/aws-sdk-go-v2/config v1.32.12
github.com/aws/aws-sdk-go-v2/credentials v1.19.12
github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.56.0
Expand All @@ -18,7 +18,7 @@ require (
github.com/aws/aws-sdk-go-v2/service/ssm v1.68.3
github.com/aws/aws-sdk-go-v2/service/sso v1.30.13
github.com/aws/aws-sdk-go-v2/service/sts v1.41.9
github.com/aws/smithy-go v1.24.2
github.com/aws/smithy-go v1.25.0
github.com/charmbracelet/bubbles v1.0.0
github.com/charmbracelet/bubbletea v1.3.10
github.com/charmbracelet/lipgloss v1.1.0
Expand All @@ -30,12 +30,13 @@ require (
github.com/atotto/clipboard v0.1.4 // indirect
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.8 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.20 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.21 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.21 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.22 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.22 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6 // indirect
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.22 // indirect
github.com/aws/aws-sdk-go-v2/service/cloudtrail v1.55.9 // indirect
github.com/aws/aws-sdk-go-v2/service/configservice v1.62.1 // indirect
github.com/aws/aws-sdk-go-v2/service/eks v1.82.1 // indirect
github.com/aws/aws-sdk-go-v2/service/elasticache v1.52.0 // indirect
github.com/aws/aws-sdk-go-v2/service/guardduty v1.75.0 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7 // indirect
Expand Down
10 changes: 10 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z
github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
github.com/aws/aws-sdk-go-v2 v1.41.5 h1:dj5kopbwUsVUVFgO4Fi5BIT3t4WyqIDjGKCangnV/yY=
github.com/aws/aws-sdk-go-v2 v1.41.5/go.mod h1:mwsPRE8ceUUpiTgF7QmQIJ7lgsKUPQOUl3o72QBrE1o=
github.com/aws/aws-sdk-go-v2 v1.41.6 h1:1AX0AthnBQzMx1vbmir3Y4WsnJgiydmnJjiLu+LvXOg=
github.com/aws/aws-sdk-go-v2 v1.41.6/go.mod h1:dy0UzBIfwSeot4grGvY1AqFWN5zgziMmWGzysDnHFcQ=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.8 h1:eBMB84YGghSocM7PsjmmPffTa+1FBUeNvGvFou6V/4o=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.8/go.mod h1:lyw7GFp3qENLh7kwzf7iMzAxDn+NzjXEAGjKS2UOKqI=
github.com/aws/aws-sdk-go-v2/config v1.32.12 h1:O3csC7HUGn2895eNrLytOJQdoL2xyJy0iYXhoZ1OmP0=
Expand All @@ -12,8 +14,12 @@ github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.20 h1:zOgq3uezl5nznfoK3ODuqb
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.20/go.mod h1:z/MVwUARehy6GAg/yQ1GO2IMl0k++cu1ohP9zo887wE=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.21 h1:Rgg6wvjjtX8bNHcvi9OnXWwcE0a2vGpbwmtICOsvcf4=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.21/go.mod h1:A/kJFst/nm//cyqonihbdpQZwiUhhzpqTsdbhDdRF9c=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.22 h1:GmLa5Kw1ESqtFpXsx5MmC84QWa/ZrLZvlJGa2y+4kcQ=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.22/go.mod h1:6sW9iWm9DK9YRpRGga/qzrzNLgKpT2cIxb7Vo2eNOp0=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.21 h1:PEgGVtPoB6NTpPrBgqSE5hE/o47Ij9qk/SEZFbUOe9A=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.21/go.mod h1:p+hz+PRAYlY3zcpJhPwXlLC4C+kqn70WIHwnzAfs6ps=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.22 h1:dY4kWZiSaXIzxnKlj17nHnBcXXBfac6UlsAx2qL6XrU=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.22/go.mod h1:KIpEUx0JuRZLO7U6cbV204cWAEco2iC3l061IxlwLtI=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6 h1:qYQ4pzQ2Oz6WpQ8T3HvGHnZydA72MnLuFK9tJwmrbHw=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6/go.mod h1:O3h0IK87yXci+kg6flUKzJnWeziQUKciKrLjcatSNcY=
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.22 h1:rWyie/PxDRIdhNf4DzRk0lvjVOqFJuNnO8WwaIRVxzQ=
Expand All @@ -30,6 +36,8 @@ github.com/aws/aws-sdk-go-v2/service/ec2 v1.296.0 h1:98Miqj16un1WLNyM1RjVDhXYumh
github.com/aws/aws-sdk-go-v2/service/ec2 v1.296.0/go.mod h1:T6ndRfdhnXLIY5oKBHjYZDVj706los2zGdpThppquvA=
github.com/aws/aws-sdk-go-v2/service/ecs v1.76.0 h1:a5G/TgJNrpuCjZBTf8/PTN0C2B0do/ylaYVynxPSbUQ=
github.com/aws/aws-sdk-go-v2/service/ecs v1.76.0/go.mod h1:QkWmubOYmjj3cHn7A4CoUU7BKJhVeo39Gp6NH7IyhZw=
github.com/aws/aws-sdk-go-v2/service/eks v1.82.1 h1:xTzXiQ8Q6U4ACdMNSCm72zd4Ds7QxhgVLqt5x8GXLBM=
github.com/aws/aws-sdk-go-v2/service/eks v1.82.1/go.mod h1:jjcGpziR11RTrr3JIgXg/Nn8GSwK3WOz2z1v/RqEBUI=
github.com/aws/aws-sdk-go-v2/service/elasticache v1.52.0 h1:inluxH5ArTlQNGrFxP7RN5o5DEfP8bRbkPC/408Esgs=
github.com/aws/aws-sdk-go-v2/service/elasticache v1.52.0/go.mod h1:DxywiXnEB21757xcql9xCqgt8vyTxSB7tVEIOdfKIY8=
github.com/aws/aws-sdk-go-v2/service/guardduty v1.75.0 h1:sZA3jpOkwrinRL0B5aZY6npTjDmmBCJRY51dee1Oh7Q=
Expand Down Expand Up @@ -66,6 +74,8 @@ github.com/aws/aws-sdk-go-v2/service/sts v1.41.9 h1:Cng+OOwCHmFljXIxpEVXAGMnBia8
github.com/aws/aws-sdk-go-v2/service/sts v1.41.9/go.mod h1:LrlIndBDdjA/EeXeyNBle+gyCwTlizzW5ycgWnvIxkk=
github.com/aws/smithy-go v1.24.2 h1:FzA3bu/nt/vDvmnkg+R8Xl46gmzEDam6mZ1hzmwXFng=
github.com/aws/smithy-go v1.24.2/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc=
github.com/aws/smithy-go v1.25.0 h1:Sz/XJ64rwuiKtB6j98nDIPyYrV1nVNJ4YU74gttcl5U=
github.com/aws/smithy-go v1.25.0/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc=
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
github.com/charmbracelet/bubbles v1.0.0 h1:12J8/ak/uCZEMQ6KU7pcfwceyjLlWsDLAxB5fXonfvc=
Expand Down
30 changes: 30 additions & 0 deletions internal/app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@ const (
screenECSServiceDetail
screenECSTaskList
screenECSContainerList
screenEKSClusterList
screenEKSNodeGroupList
screenEKSNodeGroupDetail
screenS3BucketList
screenS3ObjectList
screenS3ObjectDetail
Expand Down Expand Up @@ -233,6 +236,18 @@ type Model struct {
ecsContainers []awsservice.ECSContainer
ecsContainerIdx int

// EKS browser state
eksClusters []awsservice.EKSCluster
filteredEKSClusters []awsservice.EKSCluster
eksClusterIdx int
selectedEKSCluster *awsservice.EKSCluster

eksNodeGroups []awsservice.EKSNodeGroup
filteredEKSNodeGroups []awsservice.EKSNodeGroup
eksNodeGroupIdx int
selectedEKSNodeGroup *awsservice.EKSNodeGroup
eksNodeGroupScroll int

// Feature submodels
ec2Browser ec2InstanceBrowserModel
cwMetrics cloudWatchMetricsModel
Expand Down Expand Up @@ -448,6 +463,7 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
m.handleSecurityGroupMsg,
m.handleIAMMsg,
m.handleECSMsg,
m.handleEKSMsg,
m.handleInspectorMsg,
m.handleContextMsg,
} {
Expand Down Expand Up @@ -587,6 +603,12 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
return m.updateECSTaskList(msg)
case screenECSContainerList:
return m.updateECSContainerList(msg)
case screenEKSClusterList:
return m.updateEKSClusterList(msg)
case screenEKSNodeGroupList:
return m.updateEKSNodeGroupList(msg)
case screenEKSNodeGroupDetail:
return m.updateEKSNodeGroupDetail(msg)
case screenContextPicker:
return m.updateContextPicker(msg)
case screenContextAdd:
Expand Down Expand Up @@ -711,6 +733,8 @@ func (m Model) updateFeatureList(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
return m.startLoading(m.loadIAMKeys())
case domain.FeatureECSExec:
return m.startLoading(m.loadECSClusters())
case domain.FeatureEKSBrowser:
return m.startLoading(m.loadEKSClusters())
case domain.FeatureLambdaBrowser:
return m.lambda.Start(&m)
case domain.FeatureBedrockAPIKeys:
Expand Down Expand Up @@ -829,6 +853,12 @@ func (m Model) View() string {
v = m.viewECSTaskList()
case screenECSContainerList:
v = m.viewECSContainerList()
case screenEKSClusterList:
v = m.viewEKSClusterList()
case screenEKSNodeGroupList:
v = m.viewEKSNodeGroupList()
case screenEKSNodeGroupDetail:
v = m.viewEKSNodeGroupDetail()
case screenContextPicker:
v = m.viewContextPicker()
case screenContextAdd:
Expand Down
8 changes: 8 additions & 0 deletions internal/app/filter.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ const (
filterSecurityGroups
filterECSClusters
filterECSServices
filterEKSClusters
filterEKSNodeGroups
filterCWMetrics
filterCWLogGroups
filterCWLogStreams
Expand Down Expand Up @@ -182,6 +184,12 @@ func (m *Model) applyFilterTarget(target filterTarget) {
case filterECSServices:
m.filteredECSServices = applyFilter(m.ecsServices, m.filterValue(target))
m.ecsServiceIdx = 0
case filterEKSClusters:
m.filteredEKSClusters = applyFilter(m.eksClusters, m.filterValue(target))
m.eksClusterIdx = 0
case filterEKSNodeGroups:
m.filteredEKSNodeGroups = applyFilter(m.eksNodeGroups, m.filterValue(target))
m.eksNodeGroupIdx = 0
case filterContexts:
m.filteredCtxList = applyFilter(m.ctxList, m.filterValue(target))
m.ctxIdx = 0
Expand Down
17 changes: 17 additions & 0 deletions internal/app/help.go
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,17 @@ func (m Model) currentScreenShortcuts() []helpShortcut {
{"enter", "Start an ECS exec session for the selected container"},
{"q / esc", "Go back to the task list"},
}
case screenEKSClusterList:
return listScreenShortcuts("open managed node groups for the selected cluster", "go back to the feature list", true, true)
case screenEKSNodeGroupList:
return listScreenShortcuts("open the selected node group", "go back to the cluster list", true, true)
case screenEKSNodeGroupDetail:
return []helpShortcut{
{"↑/↓, j/k", "Scroll the node group detail"},
{"pgup / pgdn", "Scroll by one page"},
{"esc", "Go back to the node group list"},
{"q", "Go back to the feature list"},
}
case screenS3BucketList:
return listScreenShortcuts("browse the selected bucket", "go back to the feature list", true, false)
case screenS3ObjectList:
Expand Down Expand Up @@ -763,6 +774,12 @@ func (m Model) helpScreenTitle() string {
return "ECS Tasks"
case screenECSContainerList:
return "ECS Containers"
case screenEKSClusterList:
return "EKS Clusters"
case screenEKSNodeGroupList:
return "EKS Node Groups"
case screenEKSNodeGroupDetail:
return "EKS Node Group Detail"
case screenS3BucketList:
return "S3 Buckets"
case screenS3ObjectList:
Expand Down
8 changes: 8 additions & 0 deletions internal/app/messages.go
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,14 @@ type ecsExecDoneMsg struct {
err error
}

type eksClustersLoadedMsg struct {
clusters []awsservice.EKSCluster
}

type eksNodeGroupsLoadedMsg struct {
nodeGroups []awsservice.EKSNodeGroup
}

type inspectorScanLoadedMsg struct {
report *inspector.SecurityScanReport
}
Expand Down
Loading
Loading