Skip to content
Open
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
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ Architecture decision records (ADRs 001–021 + the BASELINE invariant register)
## Implementation References
<!-- AUTO-MANAGED:references — managed by the /project-init sync; do not hand-edit inside the markers. -->
Per-layer implementation references live under `docs/reference/` (index: [README](docs/reference/README.md)) — [01 Edge Network](docs/reference/01-edge-network.md) · [02 Auth](docs/reference/02-auth.md) · [03 Aurora Data](docs/reference/03-data-aurora.md) · [04 Web BFF](docs/reference/04-web-bff.md) · [05 AgentCore](docs/reference/05-agentcore.md) · [06 Workers](docs/reference/06-workers.md) · [07 EKS](docs/reference/07-eks.md).
Full overview: [docs/architecture.md](docs/architecture.md) (bilingual + mermaid) · New joiners: [docs/onboarding.md](docs/onboarding.md) · Full API index (99 routes): [docs/api-reference.md](docs/api-reference.md) · Operations: [docs/runbooks/](docs/runbooks/).
Full overview: [docs/architecture.md](docs/architecture.md) (bilingual + mermaid) · New joiners: [docs/onboarding.md](docs/onboarding.md) · Full API index (104 routes): [docs/api-reference.md](docs/api-reference.md) · Operations: [docs/runbooks/](docs/runbooks/).
<!-- /AUTO-MANAGED:references -->

Agent readiness changes require `cd agent && python3 -m pytest test_agent.py test_readiness.py -q`.
Expand Down
16 changes: 8 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ AWS + Kubernetes operations dashboard with real-time monitoring, a private Cloud

## Overview

AWSops v2 is a single-pane operations dashboard for AWS and Kubernetes, rebuilt as a Terraform-based MSA: a private edge (CloudFront VPC Origin → internal ALB → ECS Fargate), Cognito + Lambda@Edge auth, Aurora Serverless v2 persistent state, AgentCore section agents for live AWS queries, and an OOM-safe async worker tier. The previous v1 architecture (single EC2, CDK, embedded Steampipe) is being decommissioned per ADR-016 — per ADR-016 (decision records are maintained in the private upstream repository).
AWSops v2 is a single-pane operations dashboard for AWS and Kubernetes, rebuilt as a Terraform-based MSA: a private edge (CloudFront VPC Origin → internal ALB → ECS Fargate), Cognito + Lambda@Edge auth, Aurora Serverless v2 persistent state, AgentCore section agents for live AWS queries, and an OOM-safe async worker tier. The previous v1 architecture (single EC2, CDK, embedded Steampipe) was decommissioned per ADR-016 (2026-07-09); its code was removed from this tree on 2026-07-12 (decision records are maintained in the private upstream repository).

![AWSops v2 Architecture](images/awsops_arch_v2.png)

Expand All @@ -32,7 +32,7 @@ Internet -> CloudFront (TLS, Lambda@Edge Cognito auth) -> VPC Origin (https-only
-> async workers: POST /api/jobs -> SQS -> Step Functions -> Lambda or Fargate worker
```

Stats: 41 pages, 99 API routes, 110 components (`web/`), 21 consolidated ADRs, Terraform-managed (`terraform/foundation`, no CDK).
Stats: 41 pages, 104 API routes, 118 components (`web/`), 21 consolidated ADRs, Terraform-managed (`terraform/foundation`, no CDK).

> **No public ALB.** The edge is fully private — CloudFront reaches the ALB only through a VPC Origin, and the ALB only accepts traffic from CloudFront's managed security group. v2's posture is a **read-only ops dashboard + AI diagnosis**: AWS-resource mutation and autonomous remediation are FROZEN by design (ADR-005) — infra changes stay with the operator's own IaC/Change Manager, with one narrowly-scoped exception for self-healing service restarts (ADR-015). (ADR-019's SG-rules Athena role is a separate, ordinary GATED feature — ADR-019 concludes it sits inside the existing read-only invariant and is not an ADR-005 exception.)

Expand Down Expand Up @@ -176,7 +176,7 @@ AgentCore's own config (runtime ARN, Memory ID, Code Interpreter ID) is written

```
awsops/
web/ # Next.js 15 thin-BFF: 41 pages, 99 API routes, 110 components
web/ # Next.js 15 thin-BFF: 41 pages, 104 API routes, 118 components
agent/ # Strands Agent (Runtime source) + MCP Lambda tool sources
terraform/foundation/ # single Terraform root: network, edge, auth, data, workload, ai, workers, eks
scripts/v2/ # configure/deploy/migrate/agentcore/workers tooling (all Node.js/Python)
Expand Down Expand Up @@ -230,7 +230,7 @@ including the conditional documentation build and full presentation archive chec

## API Documentation

The 99 API routes live under `web/app/api/`. Key routes: `health` (public), `stream` (SSE chat), `db` (Aurora ping), `jobs` (+`/[id]`, async job submission/status), `security`, `compliance`, `auth/login`. See the docs site for user-facing guidance.
The 104 API routes live under `web/app/api/`. Key routes: `health` (public), `stream` (SSE chat), `db` (Aurora ping), `jobs` (+`/[id]`, async job submission/status), `security`, `compliance`, `auth/login`. See the docs site for user-facing guidance.

## Contributing

Expand Down Expand Up @@ -261,7 +261,7 @@ Licensed under the MIT License. See [LICENSE](LICENSE) for details.

## 개요

AWSops v2는 AWS와 Kubernetes를 위한 단일 화면 운영 대시보드로, Terraform 기반 MSA로 재구축되었습니다: 비공개 엣지(CloudFront VPC Origin → 내부 ALB → ECS Fargate), Cognito + Lambda@Edge 인증, Aurora Serverless v2 영속 상태, 라이브 AWS 조회를 수행하는 AgentCore 섹션 에이전트, OOM-안전 비동기 워커 계층으로 구성됩니다. 이전 v1 아키텍처(단일 EC2, CDK, 내장 Steampipe)는 ADR-016에 따라 폐기 진행 중입니다 — (결정 기록은 비공개 upstream 리포지토리에서 관리됩니다).
AWSops v2는 AWS와 Kubernetes를 위한 단일 화면 운영 대시보드로, Terraform 기반 MSA로 재구축되었습니다: 비공개 엣지(CloudFront VPC Origin → 내부 ALB → ECS Fargate), Cognito + Lambda@Edge 인증, Aurora Serverless v2 영속 상태, 라이브 AWS 조회를 수행하는 AgentCore 섹션 에이전트, OOM-안전 비동기 워커 계층으로 구성됩니다. 이전 v1 아키텍처(단일 EC2, CDK, 내장 Steampipe)는 ADR-016에 따라 폐기되었고(2026-07-09), 코드는 2026-07-12에 이 트리에서 제거되었습니다 (결정 기록은 비공개 upstream 리포지토리에서 관리됩니다).

![AWSops v2 Architecture](images/awsops_arch_v2.png)

Expand All @@ -272,7 +272,7 @@ Internet -> CloudFront (TLS, Lambda@Edge Cognito 인증) -> VPC Origin (https-on
-> 비동기 워커: POST /api/jobs -> SQS -> Step Functions -> Lambda 또는 Fargate 워커
```

현황: 41 페이지, 99 API 라우트, 110 컴포넌트(`web/`), 21개 통합 ADR, Terraform 관리(`terraform/foundation`, CDK 없음).
현황: 41 페이지, 104 API 라우트, 118 컴포넌트(`web/`), 21개 통합 ADR, Terraform 관리(`terraform/foundation`, CDK 없음).

> **공개 ALB 없음.** 엣지는 완전히 비공개입니다 — CloudFront는 VPC Origin을 통해서만 ALB에 도달하고, ALB는 CloudFront 관리형 보안 그룹의 트래픽만 허용합니다. v2의 자세는 **read-only 운영 대시보드 + AI 진단**입니다: AWS 리소스 변경·자율 조치는 설계상 FROZEN(ADR-005) — 인프라 변경은 운영자 자신의 IaC/Change Manager가 담당하며, 자가치유 서비스 재시작 하나만 좁게 예외 허용됩니다(ADR-015). (ADR-019의 SG-rules Athena role은 별개의 일반 GATED 기능입니다 — ADR-019는 이것이 기존 read-only 불변식 내부에 있다고 결론 내리며, ADR-005 예외가 아닙니다.)

Expand Down Expand Up @@ -411,7 +411,7 @@ AgentCore 자체 설정(runtime ARN, Memory ID, Code Interpreter ID)은 provisio

```
awsops/
web/ # Next.js 15 thin-BFF: 41 페이지, 99 API 라우트, 110 컴포넌트
web/ # Next.js 15 thin-BFF: 41 페이지, 104 API 라우트, 118 컴포넌트
agent/ # Strands Agent(Runtime 소스) + MCP Lambda 도구 소스
terraform/foundation/ # 단일 Terraform 루트: network, edge, auth, data, workload, ai, workers, eks
scripts/v2/ # configure/deploy/migrate/agentcore/workers 도구(전부 Node.js/Python)
Expand Down Expand Up @@ -465,7 +465,7 @@ controller/workflow 검사에는 Python 3·PyYAML·boto3/botocore (`pip install

## API 문서

99개 API 라우트가 `web/app/api/`에 있습니다. 주요 라우트: `health`(공개), `stream`(SSE 채팅), `db`(Aurora ping), `jobs`(+`/[id]`, 비동기 작업 제출/상태), `security`, `compliance`, `auth/login`. 사용자 가이드는 docs site를 참고하세요.
104개 API 라우트가 `web/app/api/`에 있습니다. 주요 라우트: `health`(공개), `stream`(SSE 채팅), `db`(Aurora ping), `jobs`(+`/[id]`, 비동기 작업 제출/상태), `security`, `compliance`, `auth/login`. 사용자 가이드는 docs site를 참고하세요.

## 기여 방법

Expand Down
16 changes: 15 additions & 1 deletion docs/api-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -270,10 +270,24 @@ The opt-in `/topology?view=e2e` view uses the pure `web/lib/e2e-topology.ts` mod
| `/api/dns-logs` | GET | Resolver query-log 설정 상태(메뉴 게이트) — 미설정/무권한도 200 + 빈 configs | verifyUser |
| `/api/dns-logs/analytics` | GET | Resolver 로그 집계 분석 (Logs Insights 병렬 폴링, `maxDuration` 60s, group은 라이브 allowlist 검증) | verifyUser |

## sg (1)
## sg (4)
| 경로 | 메서드 | 역할 | 인증 |
|------|--------|------|------|
| `/api/sg` | GET | Security Group 사용 분석(ENI 부착+상호참조 미사용 감지, 룰 소스/목적지 식별). `?regions=`로 리전 스코핑(안 주면 인벤토리 전 리전). `?view=hits&id=sg-...` 트래픽 히트 매칭 — Flow Logs 우선(ACCEPT만 룰 귀속), NFM 폴백은 **상대 식별 전용**(양방향 집계라 룰 귀속 불가, hits=null) | verifyUser |
| `/api/sg/rules` | GET | Filtered, paginated SG-rule inventory and activity from Aurora. Filters are bound parameters (`web/lib/sg-rules.ts`), never SQL fragments | verifyUser |
| `/api/sg/rules/refresh` | POST | Manual refresh — enqueues the same `sg_rule_scan` job the daily dispatcher uses via `enqueueJob` (not the generic `/api/jobs`, ADR-009). 503 when workers are disabled | verifyUser + isAdmin |
| `/api/sg/flow-sources` | GET, PUT | Flow Log Athena source configuration (ADR-019). GET for any user; PUT validates workgroup/database/table against allowlist regexes, and the live existence check runs in the isolated Athena broker Lambda | verifyUser (+ isAdmin on PUT) |


## network-paths (4)
All routes return 503 unless `NETWORK_PATH_CHECK_ENABLED=true` (`web/lib/network-path-gate.ts`).

| Path | Method | Role | Auth |
|------|--------|------|------|
| `/api/network-paths` | GET, POST | List saved network-path checks / create one (`created_by_sub` = caller) | verifyUser |
| `/api/network-paths/[id]` | GET, PATCH, DELETE | Read a check; edit or soft-delete it (creator or admin only, otherwise 403) | verifyUser |
| `/api/network-paths/[id]/runs` | GET, POST | Run history (most recent first, max 50; kept after soft delete) / start a run, which enqueues the `network_path` job directly (ADR-009). New runs are also refused by `networkPathLiveTopologyCapabilityGate` until live topology re-reads are available | verifyUser |
| `/api/network-path-runs/[runId]` | GET | Run status and phase; once concluded, `overall_status`, candidates, steps and the validation bundle | verifyUser |

## anfw (1)
| 경로 | 메서드 | 역할 | 인증 |
Expand Down
2 changes: 1 addition & 1 deletion web/app/CLAUDE.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# App Routes Module

## Role
Next.js App Router — 41 pages + 99 API routes (`app/api/`). APIs are thin-BFF: Aurora reads, AWS SDK reads, and AgentCore calls only. Long/OOM-risk work is enqueued — but only through allowlisted (`noop`-family) types on the generic `POST /api/jobs`; domain jobs (`report`, `compliance`, etc.) go through their own ownership-checked dedicated routes (ADR-009), never the generic one.
Next.js App Router — 41 pages + 104 API routes (`app/api/`). APIs are thin-BFF: Aurora reads, AWS SDK reads, and AgentCore calls only. Long/OOM-risk work is enqueued — but only through allowlisted (`noop`-family) types on the generic `POST /api/jobs`; domain jobs (`report`, `compliance`, etc.) go through their own ownership-checked dedicated routes (ADR-009), never the generic one.

## Structure
- Pages: overview `page.tsx`, `inventory/[type]` · `inventory/g/[group]` · `inventory/ecs` (unified ECS overview), `eks/` (overview · nodes · pods · deployments · services · explorer · cost · `[cluster]`), `topology/` (overview · infra · services · `resource/[id]`), `monitoring`, `network-flow`, `dns-query`, `ip-addresses`, `vpc-endpoints`, `direct-connect`, `network-firewall`, `sg/usage` · `sg/rules` (SG Rules & Usage, ADR-019), `network-paths` (+`[id]`, Network Path Check saved definitions/runs), `security`, `compliance`, `cost` (+FinOps baseline-recommendations card, ADR-020), `bedrock`, `agentcore`, `ai-diagnosis` (+`report` print view), `assistant`, `datasources`, `integrations` (+`datasources/[id]`), `accounts`, `customization`, `jobs`, `login`.
Expand Down
43 changes: 43 additions & 0 deletions web/lib/docs-stats.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { readFileSync, readdirSync } from 'node:fs';
import { join, relative } from 'node:path';
import { describe, it, expect } from 'vitest';

// The route/page/component counts quoted in README and CLAUDE.md drifted repeatedly (99 vs 97 vs
// the real number). Derive them from the tree so a new route fails CI until the docs follow.
const web = new URL('..', import.meta.url).pathname;
const root = join(web, '..');
const read = (p: string) => readFileSync(join(root, p), 'utf8');

function walk(dir: string, match: (name: string) => boolean): string[] {
return readdirSync(dir, { withFileTypes: true }).flatMap((e) => {
const p = join(dir, e.name);
if (e.isDirectory()) return e.name === 'node_modules' ? [] : walk(p, match);
return match(e.name) ? [p] : [];
});
}

const routes = walk(join(web, 'app/api'), (n) => n === 'route.ts')
.map((p) => '/' + relative(join(web, 'app'), p).replace(/\/route\.ts$/, ''));
const pages = walk(join(web, 'app'), (n) => n === 'page.tsx').length;
const components = walk(join(web, 'components'), (n) => n.endsWith('.tsx') && !n.endsWith('.test.tsx')).length;

describe('documented web stats match the tree', () => {
it('README quotes the actual page, route and component counts (English and Korean)', () => {
const readme = read('README.md');
expect(readme).toContain(`${pages} pages, ${routes.length} API routes, ${components} components`);
expect(readme).toContain(`${pages} 페이지, ${routes.length} API 라우트, ${components} 컴포넌트`);
expect(readme).toContain(`The ${routes.length} API routes live under`);
expect(readme).toContain(`${routes.length}개 API 라우트`);
});

it('CLAUDE.md files quote the actual route count', () => {
expect(read('CLAUDE.md')).toContain(`Full API index (${routes.length} routes)`);
expect(read('web/app/CLAUDE.md')).toContain(`${pages} pages + ${routes.length} API routes`);
});

it('docs/api-reference.md has a row for every route', () => {
const ref = read('docs/api-reference.md');
const documented = new Set([...ref.matchAll(/^\| `(\/api[^`]*)`/gm)].map((m) => m[1]));
expect(routes.filter((r) => !documented.has(r))).toEqual([]);
});
});
Loading