Content
# unic
Investigate AWS deployments, inspect resources, and switch accounts from your terminal.
**A service is ACTIVE. Its deployment has FAILED. See the difference before opening another console tab.**

[Watch the 30-second video](docs/assets/ecs-rollout.mp4) · [Still image](docs/assets/ecs-rollout.png) · [Walkthrough and recording source](docs/demo/README.md)
The walkthrough uses **synthetic data rendered by the real TUI code**. It starts with Add Context, saves a profile/region in a temporary config, then shows shipped ECS filtering, rollout details, image versions, and service events. Authentication and AWS results are simulated; it makes no AWS calls and is not a recording of a live incident.
## Get to your first rollout
Install both `unic` and its read-only MCP server:
```bash
curl -sSL https://raw.githubusercontent.com/DevopsArtFactory/unic/main/install.sh | sh
unic --profile your-profile --region us-east-1
```
Use an existing AWS profile with access to your ECS cluster. If it uses SSO, sign in with `aws sso login --profile your-profile` first. Skip the startup splash with `Space`.
### Add your first context
1. In **Select Context**, press `a` to open **Add Context**. During another session, `C` opens the context picker first.
2. Select **credential** to reuse an existing AWS profile. This path references a profile name; it does not ask you to paste access keys. For a directly configured SSO context, use **sso** instead and follow the [auth-type reference](#auth-types).
3. Enter a **Name** (for example, `dev-readonly`), skip **Display Order** with `Enter`, and enter the **Region** (for example, `us-east-1`).
4. Skip **Other Resource Regions** if unneeded, then enter your existing **Profile** name. Leave **Role ARN**, **External ID**, and **MFA Device ARN** blank unless your setup needs role chaining or MFA; `Enter` skips each optional field.
5. Review the summary and press `Enter` to save. Back in the picker, select the saved context with `Enter`. Saving is local; selecting it can authenticate with AWS. The context name alone does not make access read-only—your IAM permissions do.
Context creation currently uses this TUI wizard, not a `unic context add` CLI command. See [configuration](#configuration) for the config path and other auth options.
### Open the rollout
After selecting the context, press `P`, type `ECS Browser`, and select **ECS Browser & Exec**. Select your cluster, then your service. Press `/` to filter the service list; press `Enter` to finish filtering, then `Enter` again to open the rollout. Use `↓` or `PgDn` to reach the image versions and recent events on a shorter terminal; `?` shows help.
For a one-off session without saving a context, use the same `P` route directly from the initial picker; it retains your CLI-selected profile and region.
Having trouble connecting? Run `unic doctor --profile your-profile --region us-east-1` and follow its remediation. See [configuration](#configuration) for context setup and [Homebrew](#homebrew) or [source installation](#build-from-source) for other install options.
### Use it for the work in front of you
- **A deployment is stuck:** inspect ECS rollout state, task counts, image versions, and recent service events.
- **Something is unhealthy:** start from CloudWatch alarms and jump to related resources or logs where supported.
- **You work across accounts:** switch credential, assume-role, SSO, and Okta SAML contexts; reuse favorites and saved views.
- **You need an automated check:** run Inspector or use the [read-only CLI and MCP tools](#agent-discovery) with your local AI client.
### Credentials and changes
unic uses your selected AWS credential/profile or configured context; AWS permissions still control access. Keep credential values out of MCP configuration. **The TUI is not globally read-only:** it includes operations such as RDS start/stop, DNS changes, and key management, with confirmation for destructive workflows. Use a least-privilege role for inspection. The current MCP tools are read-only or preview-only. See [auth types](#auth-types), [MCP setup](#mcp-server), and the [security policy](SECURITY.md).
unic is built with Go, Bubble Tea, Cobra, and the AWS SDK v2. The detailed feature and setup reference follows.
## What It Does
- Browse AWS services from a single terminal UI
- Switch between credential, assume-role, SSO, and Okta SAML contexts
- Export shell environment variables for the active context
- 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, DynamoDB table and keyed-item inspection, 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 security and cost/waste 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>`
## Try Without AWS
Run `unic demo` for a synthetic ECS deployment failure without credentials, SSO,
network access, or existing configuration. Select `checkout-demo` with **Enter**,
then scroll through the failed rollout and recent service events to find the
image-pull error. Press **?** for guided steps, **Esc** to go back, and **q** to exit.
Rollout details wrap to your terminal width; **PgUp/PgDn** scroll by page and
**r** restarts the rollout view.
The demo uses fixed sample data and never reads or changes your AWS/unic config.
See [reproducible demo steps](docs/development.md#offline-ecs-demo).
## Documentation Map
This is the closest structure to a "harness-like" doc hub in this repository: one entry document with focused reference docs behind it.
- [Docs Hub](docs/README.md)
- [Documentation Harness](docs/documentation-harness.md)
- [Architecture (EN)](docs/architecture.en.md)
- [Architecture (KO)](docs/architecture.ko.md)
- [Project Overview (EN)](docs/project-overview.en.md)
- [Project Overview (KO)](docs/project-overview.ko.md)
- [Development Guide](docs/development.md)
- [Branch Naming Harness](docs/branch-naming-harness.md)
## Tech Stack
- Go 1.22+
- TUI: Bubble Tea, Bubbles, Lip Gloss
- CLI: Cobra
- AWS: aws-sdk-go-v2
- Config: gopkg.in/yaml.v3
- Logging: structured file logging under `~/.config/unic/logs/`
- Testing: Go `testing`, table-driven tests, mocked AWS clients
## Installation
### Install Script
```bash
curl -sSL https://raw.githubusercontent.com/DevopsArtFactory/unic/main/install.sh | sh
```
Set `INSTALL_DIR` to override the default install path.
The script checks that the downloaded archive contains both binaries before replacing either installation. If an older release lacks `unic-mcp`, build from source until a release containing both is available.
### Homebrew
```bash
brew tap DevopsArtFactory/unic
brew trust --formula DevopsArtFactory/unic/unic
brew install unic
```
Homebrew 6.0.0 and later require explicit trust before loading formulae from non-official taps; without the trust step `brew install unic` fails with `Refusing to load formula ... from untrusted tap`. The formula-scoped command trusts only `unic`, rather than every current and future item in the tap, which is what `brew trust DevopsArtFactory/unic` would grant.
Homebrew and release archives install both the `unic` TUI and the `unic-mcp` stdio server.
### Build From Source
```bash
git clone https://github.com/DevopsArtFactory/unic.git
cd unic
make build
mkdir -p "$HOME/.local/bin"
install -m 0755 unic unic-mcp "$HOME/.local/bin/"
export PATH="$HOME/.local/bin:$PATH"
```
Keep `$HOME/.local/bin` on `PATH` in your shell configuration and launch MCP clients from that environment so they can find `unic-mcp`. For desktop clients launched outside the shell, use the absolute path to the installed `unic-mcp` binary in their MCP configuration if they do not inherit that `PATH`.
## CLI Usage
### Run the TUI
```bash
unic
unic --profile my-profile
unic --region ap-northeast-2
unic --checklist ./checklists/readiness.yaml # optional: pre-load a checklist at startup
unic --verbose
```
The TUI shows a short retro bootup splash after a new install or version update, then records the version so later launches go straight to the context picker. Press `Enter`, `Esc`, or `Space` to skip it. Press `S` to open Settings, where the splash can be toggled on or off for every launch.
### Config/bootstrap
```bash
unic init
unic init --force
unic doctor
unic doctor --json
unic update
```
`unic doctor` checks the paired binary versions, configuration, AWS credential chain, and a local MCP initialize/tool-discovery handshake. It never prints credential values; use `--json` for its stable v1 report.
For direct binary installations, updates install `unic` and `unic-mcp` together. If the installed version predates MCP packaging (including v0.3.1), run the install script once after a newer release is published; the older updater can replace only `unic`. Homebrew installations should continue to use `brew upgrade unic`.
### Shell environment helpers
```bash
# Print exports for current context
unic env
# Print exports for a named context
eval "$(unic env prod-admin)"
# Interactively choose/setup a context and copy exports to clipboard
unic context setup
# Print an ECR login command for the current context (scripting helper;
# the TUI's ECR Login Helper is the primary workflow)
unic ecr login
# Copy a Podman ECR login command to the clipboard
unic ecr login --runtime podman --copy
# Set a display order for a context
unic context order prod-admin 10
# Or open reorder mode, then move the selected context with arrow keys and save
unic context order
# Clear current context and copy cleanup commands to clipboard
unic context unset
# Generate contexts from the accounts/roles visible to an SSO base context
unic context sync
unic context sync dev-sso --dry-run
unic context sync dev-sso --json
unic context sync dev-sso --apply --confirm dev-sso
unic context sync dev-sso --prune --apply --confirm dev-sso
```
### Agent discovery
AI agents and scripts can inspect supported AWS features and executable CLI commands without starting the TUI:
```bash
unic capabilities --json
unic schema context sync --json
```
The deterministic v1 JSON includes command arguments, flags, read-only and destructive classifications, and each command's output contract version. Service data comes from the same catalog used by the TUI.
`unic context setup` writes its prompts to `stderr` and copies the generated shell commands to the clipboard.
`unic env` prints shell commands to `stdout` so it can be used with `eval`.
`unic ecr login` prints a machine-usable container registry login command to `stdout` and can optionally copy it to the clipboard with `--copy`.
Both flows now include a `UNIC_CONTEXT` marker in the generated exports so the TUI can show which shell context is currently active.
Contexts can be prioritized in the setup picker with an `order` field in config.
In the CLI `unic context setup` flow, the picker filters contexts, SSO accounts, SSO roles, and configured resource regions as you type, with arrow-key navigation and Enter to confirm. Multi-region contexts prompt for the shell session region after account/role selection; single-region contexts skip that step. The selection changes `AWS_REGION` and `AWS_DEFAULT_REGION` in the generated exports without modifying the context's persisted default region.
Use `unic context order` to open reorder mode, choose a context with `↑/↓` or `j/k`, press `Enter` to start moving it, then press `Enter` again to save. `unic context order <name> <number>` still works for direct updates.
`unic context sync [base-context]` lists the AWS accounts and roles visible to an SSO base context and previews a sync plan without writing config. Apply the plan explicitly with `--apply --confirm <base-context>`; add `--prune` to remove sync-managed contexts that disappeared from SSO. Existing contexts are never rewritten, and new contexts inherit the base context's regions. Use `--json` for a stable v1 plan/result contract containing `before`, `after`, `changed`, and an optional rollback hint. JSON failures include a stable error code, message, retryability, and the required AWS permission when known; machine-readable output stays on stdout while diagnostics use stderr.
For automation, `unic resources <query> --json` runs a read-only AWS query using the active context (or `--profile` / `--region`) without starting the TUI. Every query returns a v1 JSON envelope with `data`, `warnings`, and pagination metadata. Available queries are:
- `backup-vaults`
- `ec2-instances`
- `rds-instances`
- `elasticache-resources`
- `sns-topics`
- `cloudformation-stacks`
- `alarms`
- `ecs-rollout --cluster <name-or-arn> --service <name-or-arn>`
- `cloudtrail-events [--since 24h] [--resource <name>] [--mutations-only]`
- `elb-target-health --load-balancer <arn>`
- `sqs-queues`
Inspector runs outside `resources` because it is a scan, not a resource listing. `unic inspect --json` runs every built-in security and cost/waste rule pack against the active context and returns the same v1 envelope, with `data` carrying `scanned_at`, `scanner_count`, `finding_count`, `severity_counts`, and the `findings` array. Rule packs that fail — most often a denied API call — appear in `warnings` rather than being dropped, so a partially blocked scan is never reported as a clean one. The equivalent MCP tool is `run_security_inspector`. The root `--checklist` flag is inherited but rejected here: Checklist Inspector produces a different report shape and has no agent contract yet, so it fails loudly rather than returning security findings in its place.
The same operations are exposed by `unic-mcp` as read-only tools. Call `get_mcp_capabilities` to discover their versioned input contracts, strict input schemas, output contracts, pagination behavior, and required IAM permissions. The operation permissions are `ec2:DescribeInstances`, `rds:DescribeDBInstances`, `elasticache:DescribeCacheClusters`, `elasticache:DescribeReplicationGroups`, `cloudwatch:DescribeAlarms`, `ecs:DescribeServices`, `ecs:DescribeTaskDefinition`, `cloudtrail:LookupEvents`, `cloudformation:DescribeStacks`, `states:ListExecutions`, `elasticloadbalancing:DescribeTargetGroups`, `elasticloadbalancing:DescribeTargetHealth`, `sns:ListTopics`, `sns:GetTopicAttributes`, `sns:ListSubscriptionsByTopic`, `sns:GetSubscriptionAttributes`, `sqs:ListQueues`, and `sqs:GetQueueAttributes`; AWS Backup retains the permissions documented below. CLI and MCP output never includes resolved credentials.
### MCP server
`unic-mcp` exposes unic's read-only automation commands to local AI agents. Install it with Homebrew or the install script above, then confirm it is on `PATH`:
```bash
command -v unic-mcp
unic doctor
```
It reads the existing unic and AWS configuration from the server process environment. Keep AWS credentials in the standard AWS credential chain; do not put credentials in MCP configuration.
Register the server for a supported client with one command:
```bash
unic mcp setup codex
unic mcp setup claude
unic mcp setup kiro
```
Add `--dry-run` to preview the exact resulting configuration. Setup preserves unrelated servers, is safe to repeat, backs up an existing file before changing it, verifies the `unic-mcp` handshake, and stores only credential variable names—never their values. It updates `~/.codex/config.toml`, `~/.claude.json`, or `~/.kiro/settings/mcp.json`, respectively.
#### Codex
Install this repository as a Codex plugin. Its MCP configuration forwards the AWS credential, profile, region, credential-provider, and configuration-path variables listed below, plus `XDG_CONFIG_HOME` for unic configuration, from the environment that launches Codex. Only variable names are stored in the plugin.
For direct registration, add this table to `~/.codex/config.toml` (or update the existing `[mcp_servers.unic]` table created by `codex mcp add unic -- unic-mcp`):
```toml
[mcp_servers.unic]
command = "unic-mcp"
args = []
env_vars = [
"AWS_ACCESS_KEY_ID", "AWS_ACCESS_KEY",
"AWS_SECRET_ACCESS_KEY", "AWS_SECRET_KEY", "AWS_SESSION_TOKEN",
"AWS_PROFILE", "AWS_DEFAULT_PROFILE",
"AWS_REGION", "AWS_DEFAULT_REGION",
"AWS_CONFIG_FILE", "AWS_SHARED_CREDENTIALS_FILE",
"AWS_WEB_IDENTITY_TOKEN_FILE", "AWS_ROLE_ARN", "AWS_ROLE_SESSION_NAME",
"AWS_CONTAINER_CREDENTIALS_RELATIVE_URI", "AWS_CONTAINER_CREDENTIALS_FULL_URI",
"AWS_CONTAINER_AUTHORIZATION_TOKEN", "AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE",
"AWS_EC2_METADATA_DISABLED", "AWS_EC2_METADATA_V1_DISABLED",
"AWS_EC2_METADATA_SERVICE_ENDPOINT", "AWS_EC2_METADATA_SERVICE_ENDPOINT_MODE",
"AWS_CA_BUNDLE", "XDG_CONFIG_HOME",
]
```
Then inspect the registration:
```bash
codex mcp get unic
```
Codex requires explicit [`env_vars` forwarding](https://developers.openai.com/codex/mcp#stdio-servers); `codex mcp add` alone does not add this list. Launch Codex from the shell containing your intended AWS environment. After changing shell exports, restart Codex from that shell so the MCP server receives the updated environment. Keep credential values out of `env` entries and `--env` arguments, which persist values in configuration. If authentication fails or the wrong profile is selected, check the forwarded variable names and Codex's launch environment.
#### Claude Code and Claude Desktop
Claude Code can load the repository plugin or register the same server directly:
```bash
claude mcp add unic --scope user -- unic-mcp
claude mcp get unic
```
For Claude Desktop and other JSON-configured MCP clients, use:
```json
{
"mcpServers": {
"unic": {
"command": "unic-mcp",
"args": []
}
}
}
```
#### Kiro
In Kiro, open **Powers**, choose **Add Custom Power**, and import this repository from GitHub. The root `plugin.json`, `mcp.json`, and `skills/` directory follow the Agent Plugins format used by Kiro Powers.
The server provides the read-only resource operations listed above plus capability discovery, Security Inspector, and context-sync preview tools. Agents should call `get_mcp_capabilities` first because it describes only operations callable through MCP, including permissions and output contracts. Example prompts:
- `Show the AWS capabilities available through unic.`
- `List my AWS Backup vaults in ap-northeast-2.`
- `Show my deepest SQS backlogs and their dead-letter queue relationships.`
- `Show failed or rollback CloudFormation stacks and their status reasons.`
- `Show the recent failed executions for this STANDARD Step Functions state machine ARN.`
- `Preview a unic context sync without changing config.`
The context-sync tool is preview-only: it never passes `--apply` or writes configuration. If a client cannot start the server, verify `unic-mcp` is on the client's `PATH` and that the required AWS profile or SSO session is available in the client process environment.
## Configuration
Primary config path:
```text
~/.config/unic/config.yaml
```
### Legacy Flat Format
```yaml
default_profile: my-profile
default_region: ap-northeast-2
```
### Context-Based Format
```yaml
current: dev-sso
defaults:
region: ap-northeast-2
favorites:
services:
- Bedrock
- ECS
contexts:
- prod-admin
views:
- name: incident-rds
context: prod-admin
service: RDS
feature: RDS Browser
filter: prod-db
ui:
boot_splash: false
last_boot_splash_version: 0.1.3
inspector:
acm_expiry_window_days: 30
required_tags:
- Owner
- Environment
- CostCenter
contexts:
- name: dev-sso
order: 10
profile: my-sso-profile
region: ap-northeast-2
auth_type: sso
sso_start_url: https://example.awsapps.com/start
- name: dev-sso-123456789012-developerrole
profile: my-sso-profile
region: ap-northeast-2
auth_type: sso
sso_start_url: https://example.awsapps.com/start
sso_account_id: "123456789012"
sso_role_name: DeveloperRole
# Preferred structured format: one identity, several resource regions
- name: production
auth:
type: sso
sso_region: us-east-1
sso_start_url: https://example.awsapps.com/start
sso_account_id: "123456789012"
sso_role_name: Admin
resources:
default_region: ap-northeast-2
regions:
- us-east-1
- eu-west-1
- name: prod-admin
order: 20
profile: base-profile
region: us-east-1
auth_type: assume_role
role_arn: arn:aws:iam::123456789012:role/Admin
external_id: optional-external-id
mfa_serial: arn:aws:iam::123456789012:mfa/my-user # optional: MFA-protected roles
- name: local-dev
profile: local-dev
region: ap-northeast-2
auth_type: console_login
- name: staging
profile: staging
region: eu-west-1
auth_type: credential
- name: okta-prod
region: ap-northeast-2
auth_type: okta_saml
okta_org_url: https://acme.okta.com
okta_app_id: amazon_aws/0oa1b2c3d4e5f6g7h8i9/272
role_arn: arn:aws:iam::123456789012:role/OktaAdmin # required when the assertion carries multiple roles
```
### Auth Types
| Auth Type | Meaning | Required Fields |
|---|---|---|
| `credential` | Use shared AWS profile credentials, optionally chaining into a role | `profile`; optional `role_arn` (+ `external_id`, `mfa_serial`) |
| `console_login` | Run `aws login` during `unic context setup`, then use the resulting profile-backed console credentials, optionally chaining into a role | `profile`; optional `role_arn` (+ `external_id`, `mfa_serial`) |
| `assume_role` | Assume a role from a base profile, optionally with MFA | `profile`, `role_arn`; optional `mfa_serial` |
| `sso` | Use AWS IAM Identity Center / SSO, reusing a valid AWS CLI SSO cache and prompting for login only when needed | `sso_start_url`, and for concrete contexts `sso_account_id`, `sso_role_name`; `profile` is optional |
| `okta_saml` | Okta SAML federation: `unic env` signs in to Okta (prompt on stderr, password without echo; `UNIC_OKTA_USERNAME`/`UNIC_OKTA_PASSWORD` for automation), exchanges the SAML assertion via `sts:AssumeRoleWithSAML`, and caches the session under `~/.config/unic/cache/okta-saml/`. The TUI reuses a valid cached session passively. Okta MFA challenges support TOTP codes and Okta Verify push in v1 | `okta_org_url`, `okta_app_id`; `role_arn` required when the assertion carries multiple roles. Passwords and MFA secrets are never stored |
The preferred context format separates `auth` from `resources`. `auth.sso_region` controls IAM Identity Center login and role-credential retrieval. `resources.default_region` is selected at startup, and `resources.regions` lists additional regions available from the global `R` region picker. Switching regions reuses the current credentials and recreates only the regional AWS clients. The EC2 Instance Browser, RDS Browser, and Lambda Browser can additionally aggregate all configured regions into a single list with `A`; rows gain a region tag, per-region failures render inline, and actions (RDS start/stop/modify, Lambda invoke) run against the row's own region.
Legacy flat fields (`auth_type`, `profile`, `region`, `regions`, `sso_region`, and related auth fields) remain supported. A context without a region list behaves as a single-region context, and an omitted SSO region still falls back to its default resource region.
TUI startup is passive for SSO contexts: it loads the context picker without launching `aws sso login`. SSO login is prompted when you explicitly select or set up an SSO context, or when an AWS-backed workflow needs credentials.
### Okta SAML Contexts
`okta_saml` contexts federate into AWS through an Okta AWS app:
```bash
# Sign in to Okta and export session credentials
eval "$(unic env okta-prod)"
# Non-interactive (CI or scripts)
UNIC_OKTA_USERNAME=user@acme.com UNIC_OKTA_PASSWORD=... unic env okta-prod
```
Runtime flow: Okta primary authentication → optional MFA challenge → SAML assertion from the app embed link → `sts:AssumeRoleWithSAML` → session credentials cached under `~/.config/unic/cache/okta-saml/` until expiry. The TUI reuses a valid cached session passively and otherwise asks you to run `unic env <context>` first — it never prompts for Okta credentials itself.
`okta_app_id` is the app-specific part of the Okta app embed link: for `https://acme.okta.com/home/amazon_aws/0oa1b2c3d4e5f6g7h8i9/272`, use `amazon_aws/0oa1b2c3d4e5f6g7h8i9/272`.
v1 limitations:
- MFA factors: TOTP (`token:software:totp`) and Okta Verify push only. TOTP is preferred when both are enrolled; push polls for approval with a 60s deadline. Other factors fail with a list of what was found.
- Role selection is deterministic: `role_arn` wins, a single assertion role is auto-selected, and multiple roles without `role_arn` produce an explicit error listing the ARNs.
- Passwords, one-time codes, and Okta session tokens are never persisted; only the exchanged AWS session credentials are cached (0600 files in a 0700 directory).
Optional context fields:
| Field | Meaning |
|---|---|
| `order` | Lower values appear first in the context setup picker. Contexts without `order` fall back after ordered entries in their existing file order. |
| `sync_source` | Name of the SSO base context that generated this context via `unic context sync`. Marks the context as sync-managed: re-syncs may prune it (with `--prune`) when its account/role disappears from SSO. Contexts without this field are never touched by sync. |
| `sso_region` | (SSO only) Region of the IAM Identity Center portal, used for SSO login and role-credential retrieval. Defaults to `region` when unset. Use it when the SSO portal and your resources live in different regions. |
| `mfa_serial` | (assume_role only) ARN of the MFA device required by the role's trust policy. CLI flows (`unic env`, `unic context setup`) prompt for a token code on stderr and cache the resulting session under `~/.config/unic/cache/assume-role/` until it expires. The TUI reuses a valid cached session passively and otherwise asks you to run `unic env <context>` first. |
| `resources.regions` / `regions` | Additional resource regions available through the global `R` picker. The default resource region is always included automatically. |
Resolution priority:
```text
CLI flags > selected context > config defaults > hardcoded default (us-east-1)
```
Context ordering:
- Lower `order` values appear first
- Contexts without `order` appear after ordered contexts
- Contexts with the same `order` keep their file order
## Current Features
### AWS Service Catalog
| Service | Feature |
|---|---|
| EC2 | SSM Session Manager |
| EC2 | Instance Browser |
| EC2 | Security Group Browser |
| EC2 | Auto Scaling Group Browser |
| VPC | VPC Browser |
| VPC | Reachability Analyzer |
| RDS | RDS Browser |
| RDS | Instance Class Modification |
| CloudFormation | Stack Browser |
| Route53 | Route53 Browser |
| Secrets Manager | Secrets Browser |
| CloudTrail | Event Lookup |
| EventBridge | Rules Browser |
| CloudWatch | Alarm Browser |
| CloudWatch | Metrics Viewer |
| CloudWatch Logs | Logs Browser |
| ECR | ECR Login Helper |
| ECR | Repository Browser |
| ECS | ECS Browser & Exec |
| EKS | Cluster & Node Group Browser |
| FIS | Experiment Template Browser |
| ElastiCache | Cluster & Replication Group Browser |
| SNS | Topic Browser |
| SQS | Queue Browser |
| ELB | Load Balancer Browser |
| Parameter Store | Parameter Browser |
| KMS | Key Browser |
| ACM | Certificate Browser |
| Step Functions | Execution Browser |
| S3 | S3 Browser |
| Lambda | Lambda Browser |
| DynamoDB | Table Browser & Key Lookup |
| AWS Backup | Recovery Browser |
| API Gateway | API Gateway v2 Browser |
| WAF | WAFv2 Web ACL Browser |
| Bedrock | API Key Manager |
| IAM | IAM User Browser |
| IAM | ListAccessKeys |
| IAM | RotateAccessKey |
### Automation
| Workflow | Status | Notes |
|---|---|---|
| `unic-mcp` MCP server | Ready | Read-only capability and AWS discovery, context-sync previews, and one-command Codex/Claude/Kiro setup |
### Inspector Mode
| Workflow | Status | Notes |
|---|---|---|
| Security Inspector | Ready | Runs built-in security and cost/waste rule packs and opens severity-filtered findings |
| Checklist Inspector | Ready | Runs a YAML checklist and reports pass/fail per check with resource context and mismatch details |
Security Inspector ships built-in rule packs for Security Group exposure, RDS encryption/public access/backups and public snapshot sharing, IAM access key age/root-account hardening/wildcard policies, Secrets Manager rotation age, KMS customer-key rotation, S3 public access/Block Public Access/versioning, CloudTrail baseline coverage, GuardDuty and AWS Config baseline controls, ElastiCache for Valkey encryption/backup/access-control checks, and cost/waste checks for unattached EIPs and EBS volumes, stopped EC2 instances, empty target groups, and EBS snapshots aged 90 days or more. The required-tag rule applies to Elastic IPs, EBS volumes and snapshots, and EC2 instances; it is disabled unless `inspector.required_tags` contains at least one tag key, and each finding names only the required keys missing from that resource.
Checklist Inspector can load a YAML file either from the Inspector-mode file picker or from `--checklist` at startup. Press `a` on the checklist results screen to add a check through type-specific prompts instead of editing YAML: pick one of the twelve rule types, fill the prompted fields (empty skips optional expectations), and the check is appended to the loaded checklist file — or a new `unic-checklist.yaml` when none is loaded — validated through the same `LoadChecklist` rules before anything is written, then the checklist reruns so the new result shows immediately. Currently supported types:
- `rds` for expected DB instance state such as status, engine, class, Multi-AZ, encryption, public access, and backup retention
- `security_group` for required or forbidden ingress/egress rule matchers
- `secret` for rotation state, KMS key ID, and required JSON value keys
- `hosted_zone` for hosted zone existence and private/public scope checks
- `route53_record` for DNS record existence, type, TTL, values, and alias target checks within `expect.zone`
- `vpc` for VPC existence, CIDR, default-VPC posture, and subnet-count checks
- `subnet` for subnet existence, optional `expect.vpc` scoping, CIDR, availability zone, and minimum available-IP checks
- `cloudwatch_log_group` for log-group existence and retention-days checks
- `cloudtrail_baseline`, `guardduty_baseline`, `config_baseline`, and `elasticache_valkey_baseline` for checklist-driven pass/fail wrappers around the built-in baseline security scanners
Minimal checklist example:
```yaml
name: Production Readiness
checks:
- type: rds
resource: prod-db
expect:
publicly_accessible: false
storage_encrypted: true
backup_retention_days: 7
- type: security_group
resource: sg-web
expect:
ingress_absent:
- protocol: tcp
from_port: 22
to_port: 22
cidr: 0.0.0.0/0
- type: secret
resource: prod/app
expect:
rotation_enabled: true
value_keys:
- username
- password
- type: route53_record
resource: api.example.internal
expect:
zone: example.internal
record_type: A
alias_target: internal-alb-123.ap-northeast-2.elb.amazonaws.com
- type: vpc
resource: main-vpc
expect:
cidr: 10.0.0.0/16
subnet_count: 2
- type: cloudwatch_log_group
resource: /aws/ecs/app
expect:
retention_days: 30
- type: cloudtrail_baseline
resource: cloudtrail
```
## TUI Navigation
### Global
| Key | Action |
|---|---|
| `j` / `k`, `↑` / `↓` | Move selection, wrapping at list boundaries |
| `Enter` | Select / drill down |
| `Esc` | Go back |
| `q` | Quit from top-level screens |
| `H` | Jump to service list |
| `i` | Enter Inspector mode from the service list |
| `C` | Open context picker |
| `R` | Switch between the active context's configured resource regions |
| `S` | Open settings |
| `P` | Open the command palette (fuzzy search across features, contexts, and indexed resources) |
| `Tab` | Toggle current-context or synced-context resource scope in the command palette |
| `V` | Open saved views (save/apply/delete repeatable feature + filter + context jumps) |
| `/` | Toggle filter mode on supported screens |
| `f` | Favorite/unfavorite the selected service or context on supported lists |
| `?` | Toggle context-aware shortcut help |
| `Ctrl+C` | Force quit |
### Service-specific highlights
Per-service shortcuts live in [Service-Specific Highlights](docs/service-specific-highlights.md).
The command palette (`P`) fuzzy-searches three kinds of items from anywhere outside text-entry screens: service features (jump straight into a browser), contexts (switch without opening the picker), and resources indexed across services. Opening the palette starts an async index of EC2 instances, RDS instances, Lambda functions, S3 buckets, ECS clusters, and Route53 zones in the current context. Press `Tab` to opt into searching the active context plus sync-managed contexts; context fan-out is bounded, rows show context and region tags, and per-context/service failures are shown inline. Matching covers names, IDs, ARNs, contexts, and regions where available. Selecting a resource in another context switches context and then jumps to the owning browser with the shared filter prefilled to that resource.
Saved views (`V`) capture repeatable operational workflows: pressing `s` on the views screen snapshots the last opened service feature, its active shared filter, and the current context under a name you type; `enter` reapplies a view in one step — switching to the view's context first when it differs — and `d` deletes one. Views persist under `views:` in `config.yaml` (fields: `name`, `context`, `service`, `feature`, `filter`); the format is additive so future fields extend it without breaking existing files.
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 context picker also supports `f`; context favorites are saved under `favorites.contexts`, displayed first in the picker, and rendered with a distinct color style while preserving the configured context order within favorite and non-favorite groups. 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, Auto Scaling groups, security groups, IAM users, VPCs, subnets, RDS instances, CloudFormation stacks, Route53 zones/records, DynamoDB tables, EventBridge rules and targets, CloudWatch metrics, CloudWatch alarms, CloudWatch log groups/streams, CloudTrail events, Secrets Manager resources, ECS clusters/services, EKS clusters/node groups/add-ons, ECR repositories/images, FIS experiment templates/history, ElastiCache replication groups/clusters, S3 buckets/objects, SNS topics/subscriptions, SQS queues, load balancers/target groups, SSM parameters, KMS keys, ACM certificates, Step Functions state machines/executions, Lambda functions, Bedrock API keys, WAFv2 Web ACLs, and the context picker.
Watch mode is available on the CloudWatch alarm list, ECS rollout detail, SQS queue list/detail, and ELB target-group/target-health screens. Press `W` to toggle opt-in background refresh and `I` to cycle the 5s, 15s, and 30s presets. The active screen keeps its selection or scroll position while data changes; leaving the screen or starting an explicit refresh stops watch mode and cancels any in-flight watch request.
API Gateway v2 lists support filtering across APIs and routes.
The EKS Browser includes a managed add-on status view for each cluster. Add-on rows show the installed version, status, and health summary, with degraded or unhealthy add-ons highlighted so core components such as CoreDNS, kube-proxy, VPC CNI, and CSI drivers are easy to spot.
The ECR Login Helper resolves the private registry URI for the active context and shows copyable Docker and Podman login commands without leaving the TUI. The copied commands are prefixed with `eval "$(unic env <context>)"` so they authenticate with the active unic context rather than whatever ambient AWS credentials the shell happens to have; `unic ecr login` remains as a secondary CLI helper for scripting. The ECR Repository Browser opens image/tag lists from each repository. Image rows include tags, digest, pushed time, and size, and mark untagged images or images older than 90 days as cleanup candidates. Image detail exposes digest and tag values for clipboard copy.
The RDS detail screen can resize an instance with `m`: unic loads the orderable DB instance classes for the instance's engine/version in the active region into a filterable picker (current class marked), then a confirmation screen shows the current and new class with a `Tab`-toggleable apply-immediately choice (default: next maintenance window) and requires typing the instance identifier before calling ModifyDBInstance. After submitting, the detail screen polls the instance status until the change settles.
The CloudFormation Stack Browser lists failed and rollback stacks first, followed by in-progress and healthy stacks. Stack detail shows status reasons, parameters, outputs, and up to 30 of the newest resource events with failure reasons. Press `d` to start drift detection; the detail view polls for up to five minutes for CloudFormation to report the resulting stack drift status and drifted-resource count.
The SNS Topic Browser lists topics name-sorted with their type, subscription counts, and encryption key. Topic detail shows the display name, KMS key, confirmed/pending/deleted subscription counts, FIFO content-deduplication posture, and delivery policies; `Enter` opens the topic's subscriptions, listed pending-first with protocol, endpoint, owner, status, and the attached dead-letter queue ARN. Counts and attributes come from per-topic `GetTopicAttributes` calls, so a topic whose policy denies that call still appears with its attributes shown as unavailable rather than as zeros, and the failures are summarised above the list. Subscription attributes are only fetched for confirmed subscriptions because SNS rejects them for the `PendingConfirmation` sentinel; failed confirmed-subscription lookups render the DLQ state as `?` instead of implying no DLQ. The browser is read-only; publishing and subscription changes are out of scope. Requires `sns:ListTopics`, `sns:GetTopicAttributes`, `sns:ListSubscriptionsByTopic`, and `sns:GetSubscriptionAttributes`.
The SQS Queue Browser is a backlog-first triage view: queues list deepest-backlog first with `!` marking dead-letter queues, showing depth, in-flight, and delayed counts per row. Queue detail shows the redrive relationship — `d` jumps from a source queue into its DLQ, and on a DLQ `m` starts a redrive (StartMessageMoveTask) moving messages back to their source queues. `x` purges a queue. Both actions require typing the queue name to confirm, and rows loaded through the all-regions scope act against their own region.
The Load Balancer Browser is a target-health-first triage view: the load balancer list (name, type, scheme, state, with `A` toggling an all-regions scope) drills into the balancer's target groups sorted unhealthiest first with per-group healthy/unhealthy/other counts, and one more `Enter` opens per-target health with unhealthy targets first, showing reason codes (e.g. `Target.Timeout`) and descriptions. Rows loaded through the all-regions scope drill into their own region.
The Parameter Store Browser lists parameter metadata (hierarchical path, type, tier, last-modified) with the shared filter matching path segments. Values are never fetched or rendered implicitly: parameter detail shows metadata with the value hidden, `v` explicitly fetches and reveals it (decrypting SecureString), and `y` fetches and copies the value straight to the clipboard without ever printing it to the terminal.
The ElastiCache Browser lists Valkey/Redis replication groups and standalone cache clusters with engine, status, node count, and node type. Opening a resource shows its cache nodes with shard, role, status, and Availability Zone; node detail exposes the connection endpoint and `c` copies it to the clipboard.
The KMS Key Browser lists key state, manager, aliases, and automatic-rotation posture. Key detail shows the ARN, origin, description, aliases, and rotation status; Security Inspector reports customer-managed keys that do not have automatic rotation enabled. If a key policy denies its rotation-status lookup, the key remains listed with rotation shown as unknown and an inline warning identifies the failed lookup.
The ACM Certificate Browser sorts certificates by soonest expiry and shows status, days remaining, in-use count, renewal eligibility, domains/SANs, validation state, and attached resource ARNs. A certificate that cannot be described is reported inline without hiding certificates that loaded successfully. Security Inspector reports certificates expiring within 30 days by default, with certificates at seven days or less raised as high severity. Set `inspector.acm_expiry_window_days` in `config.yaml` to use a different positive-day warning window.
The Step Functions Execution Browser lists state machines in the active region, then shows up to the 200 most recent STANDARD workflow executions with failed, timed-out, aborted, and pending-redrive runs ahead of running and successful runs. Execution detail shows timing, the failed state resolved from execution history, error/cause, and compact input/output previews. If execution history is unavailable or not permitted, the remaining execution detail still loads with the failed state shown as unavailable. AWS does not expose execution listing or history for EXPRESS state machines through these APIs, so unic lists them but explains that their execution history is unavailable.
The EventBridge Rules Browser lists rules from every event bus with disabled rules first, their schedule or event-pattern trigger, attached targets, and recent trigger activity. Complete event patterns are pretty-printed and vertically scrollable in rule detail. “Last Seen” is a best-effort view of the `AWS/Events` `TriggeredRules` CloudWatch metric over the previous seven days in 30-minute buckets; missing or delayed metrics are not a complete invocation audit, and missing `cloudwatch:GetMetricData` permission leaves rule and target browsing available with activity marked unavailable. Enabling or disabling an eligible customer-managed rule requires typing `event-bus/rule` and uses `events:EnableRule` or `events:DisableRule`; rules in `ENABLED_WITH_ALL_CLOUDTRAIL_MANAGEMENT_EVENTS` mode are read-only because `EnableRule` cannot restore that exact matching mode. Browsing requires `events:ListEventBuses`, `events:ListRules`, and `events:ListTargetsByRule`.
The DynamoDB Table Browser lists billing mode, provisioned capacity or on-demand mode, approximate item count and size, and global secondary index count. Table detail shows the primary key schema, GSI schemas, TTL status, and stream configuration. Press `l` to enter the complete partition key and optional sort key; unic performs exactly one `GetItem` and renders the returned item as scrollable JSON. It never scans or queries a table. The active identity needs `dynamodb:ListTables`, `dynamodb:DescribeTable`, `dynamodb:DescribeTimeToLive`, and `dynamodb:GetItem` for this flow.
The WAFv2 Web ACL Browser combines regional ACLs from the active region with global ACLs queried through the required `us-east-1` endpoint. Rows surface default action, WCU capacity, managed/total rule counts, logging state, protected-resource counts, and informational flags for logging disabled, permissive default actions, and unassociated ACLs. Detail shows priority-ordered rules, actions/overrides, visibility settings, log destinations, regional associations, Amplify associations, CloudFront distributions, and CloudFront distribution tenants. Regional and CloudFront failures, plus individual detail, logging, and association failures, remain isolated so successful results stay visible. The active identity needs `wafv2:ListWebACLs`, `wafv2:GetWebACL`, `wafv2:GetLoggingConfiguration`, `wafv2:ListResourcesForWebACL`, `cloudfront:ListDistributionsByWebACLId`, and `cloudfront:ListDistributionTenantsByCustomization`; some protected resource types require additional service-specific list permissions.
The AWS Backup Recovery Browser lists vault state, type, recovery-point count, encryption key, and Vault Lock retention metadata in the active region. Opening a vault shows failure/expiry-prioritized recovery points, protected resources with their latest backup, and failed, expired, aborted, partial, or completed-with-issues jobs from AWS Backup's recent job window. Each paginated section is independent: completed pages and other sections remain visible when a later page or one section is denied, with the failure summarized inline. The browser is read-only and requires `backup:ListBackupVaults`, `backup:ListRecoveryPointsByBackupVault`, `backup:ListProtectedResourcesByBackupVault`, and `backup:ListBackupJobs`.
The CloudTrail Event Lookup answers "who changed what, and when": recent API events list newest-first with mutations marked `*`, actor, call, and source service per row. Keys `1`-`5` switch the time window (1h/6h/24h/3d/7d), `m` restricts to mutations (server-side via the `ReadOnly=false` lookup attribute), and `n` runs a server-side resource-name lookup — CloudTrail accepts one lookup attribute per call, so combining both applies the mutations restriction client-side. Results are capped at 100 events per query, so narrow the window or use the resource lookup when a busy account truncates. Event detail shows actor, source, region, source IP, touched resources, and the full raw event JSON with scrolling.
The CloudWatch Alarm Browser is an alarm-first incident entry point: alarms list firing-first (ALARM, then INSUFFICIENT_DATA, then OK) with a `tab`-cycled state filter and text filtering across names, states, metrics, and dimensions. Alarm detail shows the state reason, condition, dimensions, and the most recent state transitions. When an alarm's dimensions map to a supported browser (`DBInstanceIdentifier`, `InstanceId`, `ClusterName`, `FunctionName`, `LoadBalancer`, `TargetGroup`), `g` jumps into that resource browser with the filter prefilled to the unhealthy resource (for ELB alarms the target group filter is prefilled too, so the drill-down lands on the alarmed target group), and `l` opens CloudWatch Logs prefilled with the derived log group (e.g. `/aws/lambda/<function>`).
The FIS Experiment Template Browser lists experiment templates in the active region and opens a detail screen with role ARN, targets, actions, target mappings, parameters, filters, and stop condition summaries without leaving the TUI. Template detail includes a Safe Run Preview that summarizes blast radius, target selection modes, action count, active stop conditions, IAM role, and warnings for missing stop conditions, missing role ARN, broad selection, or unbounded selectors. The preview also states the template ID that any future execution path must type to confirm before a run can start. Press `h` on a selected template or template detail to inspect recent runs for that template, or `H` from the template list to inspect recent experiment history across the active account/region. History rows include run status, timing, and stop/failure summaries, with failed, stopped, stopping, and cancelled runs visually highlighted; `Enter` opens run detail with start/end times, duration, action states, targets, stop conditions, and failure metadata.
The EKS Browser includes a current-version upgrade readiness view for each selected cluster. It compares the control plane version with managed node group versions, checks installed managed add-on versions against EKS compatibility metadata for the current cluster version, includes EKS `UPGRADE_READINESS` insights, and highlights blockers or warnings before planning a target-version upgrade.
The EKS Browser includes an access helper for each selected cluster. Press `u` from the cluster list to review the cluster endpoint, ARN, current region/profile, and copy an `aws eks update-kubeconfig` command or a `kubectl get nodes` smoke-check command for handoff into Kubernetes workflows.
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. For multi-region contexts, press `A` to toggle an all-regions scope that lists instances from every configured resource region in one view: rows gain a region tag, per-region API failures are shown inline without hiding other regions' results, and detail and related-resource drill-downs query the selected instance's own region. The detail screen shows core metadata including instance ID, name tag, state, instance type, AZ, VPC, subnet, security groups, private and public IPs, launch time, platform details, IAM profile, and tags. From instance detail, related-resource drill-down screens open attached security groups (`g`), Auto Scaling membership (`a`), registered target groups (`t`), associated load balancers (`b`), and listeners (`n`). Related lists support filtering, refresh, wrap navigation, empty states for missing associations, and inline errors when a relationship cannot be loaded because of API or permission failures.
The Auto Scaling Group Browser lists desired, minimum, and maximum capacity alongside instance and healthy-instance counts. Group detail shows each instance's lifecycle and health state plus the 20 most recent scaling activities, including AWS failure messages when an activity fails. Press `c` to enter a new desired capacity within the group's current min/max bounds; unic warns that this can launch or terminate instances and requires the exact group name before calling `SetDesiredCapacity`.
Bedrock API key management uses the active unic AWS context and IAM service-specific credential APIs for `bedrock.amazonaws.com`. The TUI lists long-term Bedrock API key metadata, opens a detail screen for inspection, defaults new key generation to the current IAM user when that user can be inferred from caller identity, and keeps another-user generation as an explicit option. Creation supports an optional expiration period, where blank or `0` means no expiration, rotates secrets with a one-time result screen, and deletes keys only after typed confirmation. Generated and rotated key values are intentionally copy-only and are not printed to the terminal; on the result screen, `c` copies the key and `e` copies `export AWS_BEARER_TOKEN_BEDROCK=...`.
Reachability Analyzer starts with a region selection step, defaults to the current context region, and now surfaces the AWS-documented source and destination resource types that unic supports: EC2 instances, Internet gateways, Network interfaces, Transit gateways, Transit gateway attachments, Virtual private gateways, VPC endpoint services, VPC endpoints, VPC peering connections, plus IP addresses as destinations. The source and destination pickers support type tabs, keyword filtering, IPv4 destination validation, and automatic cleanup of temporary Network Insights resources after each analysis. During analysis, the loading screen shows a vertical source-to-destination flow and intent summary, and the result view renders path hops and findings in a more readable layout.
## Product Principles
- **TUI-first.** New user-facing AWS service capabilities ship with a TUI entry point first. CLI commands are secondary surfaces for scripting, automation, or copy/paste handoff, and should be documented as such.
## Development
```bash
go run ./cmd/unic
go test ./...
make build
```
The HOL Plugin Scanner checks the agent-plugin package on pull requests to `main` and pushes to `main`. Its job summary shows the score, analyzer status, and finding locations, including failed scans; the downloadable JSON report contains full details. See the [scanner workflow guidance](docs/development.md#plugin-scanner-ci) for thresholds and local reproduction.
Release artifacts are produced through GoReleaser and the `dist/` outputs.
GitHub Actions use full commit SHA pins; see the [maintenance guidance](docs/development.md#github-actions).
## Community Standards
- [Code of Conduct](CODE_OF_CONDUCT.md)
- [Contributing Guide](CONTRIBUTING.md)
- [Security Policy](SECURITY.md)
- [License](LICENSE)
## Issue Bot
Comment on an issue with:
| Command | Action |
|---|---|
| `@unic-bot: assign me` | Assign the issue to yourself |
## Contributors
<p align="left">
<a href="https://github.com/nathanhuh">
<img src="https://avatars.githubusercontent.com/u/23186038?v=4" width="72" alt="nathanhuh" />
</a>
<a href="https://github.com/YoungJinJung">
<img src="https://avatars.githubusercontent.com/u/18644538?v=4" width="72" alt="YoungJinJung" />
</a>
<a href="https://github.com/jjjjjjeonda86">
<img src="https://avatars.githubusercontent.com/u/37580012?v=4" width="72" alt="jjjjjjeonda86" />
</a>
</p>
- [nathanhuh](https://github.com/nathanhuh)
- [YoungJinJung](https://github.com/YoungJinJung)
- [jjjjjjeonda86](https://github.com/jjjjjjeonda86)
Connection Info
You Might Also Like
No recommendations available