{"id":50655297,"url":"https://github.com/tatematsu-k/sandbox-hosting","last_synced_at":"2026-06-07T23:30:39.440Z","repository":{"id":362636716,"uuid":"1259776575","full_name":"tatematsu-k/sandbox-hosting","owner":"tatematsu-k","description":"IP-restricted HTML sandbox host on AWS (S3 + CloudFront + Lambda + DynamoDB), driven from Slack and Claude Code skill.","archived":false,"fork":false,"pushed_at":"2026-06-05T06:09:59.000Z","size":166,"stargazers_count":0,"open_issues_count":3,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-05T08:05:43.140Z","etag":null,"topics":["aws","claude-code","cloudfront","dynamodb","html-hosting","lambda","sandbox","terraform"],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tatematsu-k.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-04T21:07:23.000Z","updated_at":"2026-06-05T06:07:10.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/tatematsu-k/sandbox-hosting","commit_stats":null,"previous_names":["tatematsu-k/sandbox-hosting"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/tatematsu-k/sandbox-hosting","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tatematsu-k%2Fsandbox-hosting","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tatematsu-k%2Fsandbox-hosting/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tatematsu-k%2Fsandbox-hosting/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tatematsu-k%2Fsandbox-hosting/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tatematsu-k","download_url":"https://codeload.github.com/tatematsu-k/sandbox-hosting/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tatematsu-k%2Fsandbox-hosting/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34042554,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-07T02:00:07.652Z","response_time":124,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["aws","claude-code","cloudfront","dynamodb","html-hosting","lambda","sandbox","terraform"],"created_at":"2026-06-07T23:30:37.911Z","updated_at":"2026-06-07T23:30:39.431Z","avatar_url":"https://github.com/tatematsu-k.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Sandbox Hosting (AWS)\n\nIP制限付きの個人向けHTMLホスティング。AWS S3 + CloudFront + Lambda + DynamoDB 構成。\nSlack slash command と Claude Code skill からアップロードする。\n\n- アーキテクチャレビュー: [docs/architecture-review.md](docs/architecture-review.md)\n- 運用者ガイド: \u003chttps://tatematsu-k.github.io/sandbox-hosting/\u003e（ソース: [docs/site/index.html](docs/site/index.html)）\n- Slack 設定: [docs/slack-setup.md](docs/slack-setup.md)\n- CI/CD 設定: [docs/cicd-setup.md](docs/cicd-setup.md)\n- Claude Code skill: [skills/sandbox-upload/SKILL.md](skills/sandbox-upload/SKILL.md)\n\n## アーキテクチャ\n\n```\nViewer ──HTTPS──► CloudFront ──OAC──► S3 (private)\n                       │\n                       └─ CloudFront Function (viewer-request)\n                            ├─ IP allowlist (CIDR/v4/v6)\n                            └─ URI rewrite (/foo/ → /published/foo/index.html)\n\nSlack / Claude Code ──HTTPS──► API Gateway HTTP API ──► Lambda (api)\n                                                            │\n                                                            ├─ S3 (read/write)\n                                                            ├─ DynamoDB (meta)\n                                                            └─ SSM Parameter Store (secrets)\n\nEventBridge daily ──► Lambda (cron)\n                          └─ scan DynamoDB → unpublish expired\n```\n\n## エンドポイント\n\n| Method | Path | 認証 | 用途 |\n| --- | --- | --- | --- |\n| GET | `https://{cdn}/{path}/[file]` | CloudFront Function | 公開HTML/アセット配信 |\n| POST | `https://{api}/upload` | Bearer `UPLOAD_TOKEN` | Claude Code からアップロード |\n| POST | `https://{api}/list` | 同上 | サイト一覧 |\n| POST | `https://{api}/activate` | 同上 (owner一致) | 再公開 / TTLリセット |\n| POST | `https://{api}/delete` | 同上 (owner一致) | 完全削除 |\n| POST | `https://{api}/slack/upload` | Slack 署名 | slash command 受け口 |\n\n## ストレージレイアウト\n\nS3 `${CONTENT_BUCKET}`:\n- `published/{path}/index.html` 他のアセット\n- `unpublished/{path}/...` TTL切れ後の退避\n\nDynamoDB `${META_TABLE}`:\n- PK: `path`\n- GSI `owner-index`: PK=`owner`\n- Item: `{path, owner, type, status, createdAt, updatedAt, ttlExpiresAt, files, source}`\n\n## ローカル開発\n\n```bash\nnpm install\nnpm run typecheck\nnpm test\nnpm run build   # esbuild で dist/{api,cron}/index.mjs を生成\n```\n\n## デプロイ\n\n### 初回（運用者）\n\n```bash\naws configure            # アクセスキー or SSO\ncd terraform\ncp terraform.tfvars.example terraform.tfvars\n# allowed_ips, alarm_email を編集\ncd ..\n./scripts/setup-aws.sh   # ビルド → terraform apply まで一括\n```\n\napply 後、以下を手動で実施:\n\n1. **Slack signing secret を SSM に投入** （初期値は `REPLACE_ME` のまま）\n   ```bash\n   aws ssm put-parameter --name \"/sandbox-hosting/SLACK_SIGNING_SECRET\" \\\n     --type SecureString --overwrite --value \"\u003csigning secret\u003e\"\n   ```\n\n2. **（任意）Slack bot token** （files.slack.com のプライベートファイル取得時のみ必要）\n   ```bash\n   aws ssm put-parameter --name \"/sandbox-hosting/SLACK_BOT_TOKEN\" \\\n     --type SecureString --overwrite --value \"xoxb-...\"\n   ```\n   未設定（`REPLACE_ME` のまま）の場合、Slack 経路の zip 取得は public file のみ対応。\n\n3. **`UPLOAD_TOKEN` を控える**（Claude Code クライアントに配布）\n   ```bash\n   aws ssm get-parameter --name \"/sandbox-hosting/UPLOAD_TOKEN\" \\\n     --with-decryption --query 'Parameter.Value' --output text\n   ```\n\n4. （任意）独自ドメインを当てる場合は `var.public_base_url` を実値で更新し、\n   ACM 証明書を us-east-1 に発行、CloudFront に紐付け（次フェーズで Terraform 拡張予定）\n\n## 既知の制約\n\n- **Slack 3秒タイムアウト**: アップロード処理が cold start + S3 + DynamoDB で 3秒を超えると Slack 側がタイムアウト表示する。warm時は通常 \u003c 1秒。\n- **Slack `\u003cURL\u003e` 形式**: Slack はテキスト中の URL を `\u003chttps://...|表示名\u003e` で wrap することがあるが、現状の `parseSlackText` は raw URL のみ対応。`file_url` パラメータ経由を推奨。\n- **Custom path concurrent upload**: 同一 custom path に対する複数同時 upload は競合 → 最後勝ち。普通の運用では発生しない想定。\n- **CloudFront Function サイズ上限 10KB**: IP allowlist の CIDR が極端に多い（数百件超）と上限に当たる可能性。その場合は WAF IP set への移管を検討。\n\n### クライアント初期設定（各利用者）\n\n```bash\n./scripts/setup-client.sh\n./scripts/healthcheck.sh\n```\n\n### 継続的インテグレーション\n\n`.github/workflows/`:\n- `ci.yml`: PR / push で typecheck + vitest + terraform fmt/validate + shellcheck\n- `pages.yml`: `docs/site/**` の変更で運用ガイドを GitHub Pages へ自動 deploy\n- `dependabot-auto-merge.yml`: patch / minor の Dependabot PR を CI 通過後に squash auto-merge\n\n本番デプロイは `vercel deploy` ではなくローカルから `./scripts/setup-aws.sh`\n（または `terraform -chdir=terraform apply`）で実行する運用。\n\n## TTL ルール\n\n- auto path (timestamp_username): 90日後に EventBridge cron が unpublish\n- custom path: TTL対象外\n- `POST /activate` で再公開 + TTLリセット\n\n## テスト\n\n```bash\nnpm run typecheck   # tsc\nnpm test            # vitest\nnpm run lint:tf     # terraform fmt -check\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftatematsu-k%2Fsandbox-hosting","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftatematsu-k%2Fsandbox-hosting","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftatematsu-k%2Fsandbox-hosting/lists"}