@@ -2,7 +2,7 @@ |
| 2 | | 2 | |
| 3 | shithub has two storage layers: | 3 | shithub has two storage layers: |
| 4 | | 4 | |
| 5 | -1. **Object storage** — S3-compatible (MinIO in dev/test, DigitalOcean Spaces in prod). Used for avatars, attachments, and (post-MVP) LFS objects. | 5 | +1. **Object storage** — S3-compatible (MinIO in dev/test, DigitalOcean Spaces in prod). Used for avatars, attachments, and (post-MVP) LFS objects. The production bucket is DigitalOcean Spaces; the `s3` naming reflects the compatible API, not AWS. |
| 6 | 2. **Repo filesystem storage** — bare git repositories on a local block-storage volume, in a sharded layout owned by the `RepoFS` helper. | 6 | 2. **Repo filesystem storage** — bare git repositories on a local block-storage volume, in a sharded layout owned by the `RepoFS` helper. |
| 7 | | 7 | |
| 8 | Both layers live behind the package `internal/infra/storage`. Path validation is the **security boundary** — every entry that takes user-supplied owner/repo names goes through `RepoPath`, which rejects unsafe inputs against a strict whitelist. If repo paths can be tricked, every later sprint inherits the bug; the test suite *over*-tests this. | 8 | Both layers live behind the package `internal/infra/storage`. Path validation is the **security boundary** — every entry that takes user-supplied owner/repo names goes through `RepoPath`, which rejects unsafe inputs against a strict whitelist. If repo paths can be tricked, every later sprint inherits the bug; the test suite *over*-tests this. |
@@ -29,16 +29,17 @@ Two implementations: |
| 29 | | 29 | |
| 30 | ### Bucket / key scheme | 30 | ### Bucket / key scheme |
| 31 | | 31 | |
| 32 | -Single bucket per environment: `shithub-dev`, `shithub-staging`, `shithub-prod`. Per-scope key prefixes ease policy and tenant isolation: | 32 | +Single bucket per environment: `shithub-dev`, `shithub-staging`, `shithub-prod`. In production this is a DigitalOcean Spaces bucket configured through the S3-compatible client. Per-scope key prefixes ease policy and tenant isolation: |
| 33 | | 33 | |
| 34 | ``` | 34 | ``` |
| 35 | lfs/<owner>/<repo>/<sha256> # LFS objects (post-MVP, key shape reserved) | 35 | lfs/<owner>/<repo>/<sha256> # LFS objects (post-MVP, key shape reserved) |
| 36 | attachments/<scope>/<id>/<filename> # issue/PR/comment attachments | 36 | attachments/<scope>/<id>/<filename> # issue/PR/comment attachments |
| 37 | -avatars/<owner>/<size>.<ext> # rendered avatar variants | 37 | +avatars/<user_id>/<hash>.png # largest rendered avatar variant |
| | 38 | +avatars/<user_id>/<hash>-<size>.png # smaller rendered avatar variants |
| 38 | backups/... # S37 | 39 | backups/... # S37 |
| 39 | ``` | 40 | ``` |
| 40 | | 41 | |
| 41 | -Keys are always lowercase. | 42 | +Avatar uploads are decoded from PNG, JPEG, or GIF and re-encoded to PNG before storage. Keys are always lowercase. |
| 42 | | 43 | |
| 43 | ### Semantics worth knowing | 44 | ### Semantics worth knowing |
| 44 | | 45 | |
@@ -107,7 +108,7 @@ All storage settings flow through `internal/infra/config` (see `docs/internal/co |
| 107 | | Key | Type | Default | Notes | | 108 | | Key | Type | Default | Notes | |
| 108 | |---|---|---|---| | 109 | |---|---|---|---| |
| 109 | | `storage.repos_root` | string | `/data/repos` | Filesystem root for bare repos. Required. | | 110 | | `storage.repos_root` | string | `/data/repos` | Filesystem root for bare repos. Required. | |
| 110 | -| `storage.s3.endpoint` | string | `""` | Host[:port], no scheme. Empty disables S3. | | 111 | +| `storage.s3.endpoint` | string | `""` | Host[:port], no scheme. Empty disables object storage. Production uses the DigitalOcean Spaces endpoint. | |
| 111 | | `storage.s3.region` | string | `us-east-1` | Region for SigV4 signing. | | 112 | | `storage.s3.region` | string | `us-east-1` | Region for SigV4 signing. | |
| 112 | | `storage.s3.access_key_id` | string | `""` | | | 113 | | `storage.s3.access_key_id` | string | `""` | | |
| 113 | | `storage.s3.secret_access_key` | string | `""` | Redacted by `config print`. | | 114 | | `storage.s3.secret_access_key` | string | `""` | Redacted by `config print`. | |