···9595 if !strings.Contains(body, "vuln-box-critical") {
9696 t.Error("Expected body to contain vuln-box-critical for critical vulnerabilities")
9797 }
9898- if !strings.Contains(body, `data-tip="Critical">2<`) {
9898+ if !strings.Contains(body, `aria-label="2 critical">2<`) {
9999 t.Error("Expected critical count of 2")
100100 }
101101 if !strings.Contains(body, "vuln-box-high") {
102102 t.Error("Expected body to contain vuln-box-high for high vulnerabilities")
103103 }
104104- if !strings.Contains(body, `data-tip="High">5<`) {
104104+ if !strings.Contains(body, `aria-label="5 high">5<`) {
105105 t.Error("Expected high count of 5")
106106 }
107107- if !strings.Contains(body, `data-tip="Medium">10<`) {
107107+ if !strings.Contains(body, `aria-label="10 medium">10<`) {
108108 t.Error("Expected medium count of 10")
109109 }
110110- if !strings.Contains(body, `data-tip="Low">3<`) {
110110+ if !strings.Contains(body, `aria-label="3 low">3<`) {
111111 t.Error("Expected low count of 3")
112112 }
113113 // Should show vulnerability strip with tooltip
···274274275275 body := rr.Body.String()
276276277277- if !strings.Contains(body, `data-tip="Critical">3<`) {
277277+ if !strings.Contains(body, `aria-label="3 critical">3<`) {
278278 t.Error("Expected critical count of 3")
279279 }
280280 // Zero-count badges should NOT appear
···353353 }
354354355355 // abc123 should have vulnerability badges
356356- if !strings.Contains(body, `data-tip="Critical">2<`) {
356356+ if !strings.Contains(body, `aria-label="2 critical">2<`) {
357357 t.Error("Expected critical count of 2 for abc123")
358358 }
359359 // def456 should have clean badge
···496496 if !strings.Contains(body, `id="scan-badge-abc123"`) {
497497 t.Error("Expected OOB span for abc123")
498498 }
499499- if !strings.Contains(body, `data-tip="Critical">1<`) {
499499+ if !strings.Contains(body, `aria-label="1 critical">1<`) {
500500 t.Error("Expected critical count of 1")
501501 }
502502}
+3-3
pkg/appview/handlers/vuln_details_test.go
···223223 }
224224225225 // Should contain "-" placeholder for unfixed vuln
226226- if !strings.Contains(body, `opacity-40`) {
227227- t.Error("Expected body to contain opacity-40 placeholder for unfixed vulnerability")
226226+ if !strings.Contains(body, `text-base-content/40`) {
227227+ t.Error("Expected body to contain dimmed placeholder for unfixed vulnerability")
228228 }
229229230230 // Should contain a table
···256256 if !strings.Contains(body, "vuln-box-critical") {
257257 t.Error("Expected body to contain vuln-box-critical in summary")
258258 }
259259- if !strings.Contains(body, `data-tip="Critical">2<`) {
259259+ if !strings.Contains(body, `aria-label="2 critical">2<`) {
260260 t.Error("Expected critical count of 2 in summary")
261261 }
262262
···88<body>
99 {{ template "nav" . }}
10101111- <main class="container mx-auto px-4 py-8 max-w-4xl">
1111+ <main id="main-content" class="container mx-auto px-4 py-8 max-w-4xl">
1212 <h1 class="text-3xl font-display font-bold tracking-tight mb-2">Privacy Policy - {{ .CompanyName }} ({{ .SiteURL }})</h1>
1313 <p class="text-base-content/60 mb-8"><em>Last updated: January 2025</em></p>
1414···1717 <h2 class="text-xl font-semibold text-primary">Data We Collect and Store</h2>
18181919 <h3 class="text-lg font-medium mt-4">Data Stored on Your PDS (Controlled by You)</h3>
2020- <p>When you use {{ .CompanyName }}, records are written to your Personal Data Server (PDS) under the <code class="bg-base-200 px-1.5 py-0.5 rounded text-sm font-mono">io.atcr.*</code> namespace. This data is stored on infrastructure you or your PDS hosting provider controls. We do not control this data, and its retention and deletion is governed by your PDS provider's policies.</p>
2020+ <p>When you use {{ .CompanyName }}, records are written to your Personal Data Server (PDS) under the <code class="bg-base-300 px-1.5 py-0.5 rounded text-sm font-mono">io.atcr.*</code> namespace. This data is stored on infrastructure you or your PDS hosting provider controls. We do not control this data, and its retention and deletion is governed by your PDS provider's policies.</p>
21212222 <h3 class="text-lg font-medium mt-6">Data Stored on Our Infrastructure</h3>
23232424- <p><strong>Layer Records:</strong> Our hold services (e.g., <code class="bg-base-200 px-1.5 py-0.5 rounded text-sm font-mono">hold01.{{ .SiteURL }}</code>) maintain records in their embedded PDS that reference container image layers you publish. These records are public and link your AT Protocol identity (DID) to content-addressed SHA identifiers.</p>
2424+ <p><strong>Layer Records:</strong> Our hold services (e.g., <code class="bg-base-300 px-1.5 py-0.5 rounded text-sm font-mono">hold01.{{ .SiteURL }}</code>) maintain records in their embedded PDS that reference container image layers you publish. These records are public and link your AT Protocol identity (DID) to content-addressed SHA identifiers.</p>
25252626 <p><strong>OCI Blobs:</strong> Container image layers are stored in our object storage (S3). These blobs are content-addressed and deduplicated—meaning identical layers uploaded by different users are stored only once.</p>
2727···5858 </ul>
59596060 <h3 class="text-lg font-medium mt-6">{{ .ClientShortName }}-Hosted Hold Services</h3>
6161- <p>Storage backends we operate (e.g., <code class="bg-base-200 px-1.5 py-0.5 rounded text-sm font-mono">hold01.{{ .SiteURL }}</code>). Each hold has an embedded PDS and stores:</p>
6161+ <p>Storage backends we operate (e.g., <code class="bg-base-300 px-1.5 py-0.5 rounded text-sm font-mono">hold01.{{ .SiteURL }}</code>). Each hold has an embedded PDS and stores:</p>
6262 <ul class="list-disc list-inside space-y-1 ml-4">
6363 <li>OCI blobs (container image layers) in object storage</li>
6464 <li>Layer records in the hold's embedded PDS linking your DID to blob references</li>
6565 <li>Crew membership records for access control</li>
6666 </ul>
6767- <p class="mt-2">Hold services on <code class="bg-base-200 px-1.5 py-0.5 rounded text-sm font-mono">*.{{ .SiteURL }}</code> domains are operated by us and covered by this policy.</p>
6767+ <p class="mt-2">Hold services on <code class="bg-base-300 px-1.5 py-0.5 rounded text-sm font-mono">*.{{ .SiteURL }}</code> domains are operated by us and covered by this policy.</p>
68686969 <h3 class="text-lg font-medium mt-6">User-Deployed Hold Services (BYOS)</h3>
7070 <p>You may use "Bring Your Own Storage" by deploying your own hold service. Data on user-deployed holds is governed by that operator's privacy policy, not ours. We can request deletion on your behalf but cannot guarantee it for services we do not control.</p>
···133133 </ul>
134134135135 <p class="mt-4"><strong>Optional: Delete AT Protocol Records</strong></p>
136136- <p>When deleting your account, you may optionally authorize us to delete <code class="bg-base-200 px-1.5 py-0.5 rounded text-sm font-mono">io.atcr.*</code> records from your PDS. This requires an active OAuth session and is optional because:</p>
136136+ <p>When deleting your account, you may optionally authorize us to delete <code class="bg-base-300 px-1.5 py-0.5 rounded text-sm font-mono">io.atcr.*</code> records from your PDS. This requires an active OAuth session and is optional because:</p>
137137 <ul class="list-disc list-inside space-y-1 ml-4">
138138 <li>Your PDS is controlled by you or your hosting provider, not us</li>
139139 <li>You may delete these records yourself at any time</li>
···186186 </thead>
187187 <tbody>
188188 <tr>
189189- <td data-label="Category">Identifiers</td>
190190- <td data-label="Examples">DID, handle, IP address, device name</td>
191191- <td data-label="Collected">Yes</td>
189189+ <td data-label="Category" aria-label="Category: Identifiers">Identifiers</td>
190190+ <td data-label="Examples" aria-label="Examples: DID, handle, IP address, device name">DID, handle, IP address, device name</td>
191191+ <td data-label="Collected" aria-label="Collected: Yes">Yes</td>
192192 </tr>
193193 <tr>
194194- <td data-label="Category">Internet activity</td>
195195- <td data-label="Examples">Access logs, usage data, actions performed</td>
196196- <td data-label="Collected">Yes</td>
194194+ <td data-label="Category" aria-label="Category: Internet activity">Internet activity</td>
195195+ <td data-label="Examples" aria-label="Examples: Access logs, usage data, actions performed">Access logs, usage data, actions performed</td>
196196+ <td data-label="Collected" aria-label="Collected: Yes">Yes</td>
197197 </tr>
198198 <tr>
199199- <td data-label="Category">Geolocation</td>
200200- <td data-label="Examples">Approximate location via IP</td>
201201- <td data-label="Collected">Yes</td>
199199+ <td data-label="Category" aria-label="Category: Geolocation">Geolocation</td>
200200+ <td data-label="Examples" aria-label="Examples: Approximate location via IP">Approximate location via IP</td>
201201+ <td data-label="Collected" aria-label="Collected: Yes">Yes</td>
202202 </tr>
203203 </tbody>
204204 </table>
···219219 </thead>
220220 <tbody>
221221 <tr>
222222- <td data-label="Data Type">OAuth tokens</td>
223223- <td data-label="Service">AppView</td>
224224- <td data-label="Retention">Until revoked or logout</td>
222222+ <td data-label="Data Type" aria-label="Data Type: OAuth tokens">OAuth tokens</td>
223223+ <td data-label="Service" aria-label="Service: AppView">AppView</td>
224224+ <td data-label="Retention" aria-label="Retention: Until revoked or logout">Until revoked or logout</td>
225225 </tr>
226226 <tr>
227227- <td data-label="Data Type">Web UI session tokens</td>
228228- <td data-label="Service">AppView</td>
229229- <td data-label="Retention">Until logout or expiration</td>
227227+ <td data-label="Data Type" aria-label="Data Type: Web UI session tokens">Web UI session tokens</td>
228228+ <td data-label="Service" aria-label="Service: AppView">AppView</td>
229229+ <td data-label="Retention" aria-label="Retention: Until logout or expiration">Until logout or expiration</td>
230230 </tr>
231231 <tr>
232232- <td data-label="Data Type">Device tokens (credential helper)</td>
233233- <td data-label="Service">AppView</td>
234234- <td data-label="Retention">Until revoked by user</td>
232232+ <td data-label="Data Type" aria-label="Data Type: Device tokens (credential helper)">Device tokens (credential helper)</td>
233233+ <td data-label="Service" aria-label="Service: AppView">AppView</td>
234234+ <td data-label="Retention" aria-label="Retention: Until revoked by user">Until revoked by user</td>
235235 </tr>
236236 <tr>
237237- <td data-label="Data Type">Cached PDS data</td>
238238- <td data-label="Service">AppView</td>
239239- <td data-label="Retention">Refreshed periodically; deleted on account deletion</td>
237237+ <td data-label="Data Type" aria-label="Data Type: Cached PDS data">Cached PDS data</td>
238238+ <td data-label="Service" aria-label="Service: AppView">AppView</td>
239239+ <td data-label="Retention" aria-label="Retention: Refreshed periodically; deleted on account deletion">Refreshed periodically; deleted on account deletion</td>
240240 </tr>
241241 <tr>
242242- <td data-label="Data Type">Server logs</td>
243243- <td data-label="Service">AppView</td>
244244- <td data-label="Retention">Currently ephemeral; this policy will be updated if log retention is implemented</td>
242242+ <td data-label="Data Type" aria-label="Data Type: Server logs">Server logs</td>
243243+ <td data-label="Service" aria-label="Service: AppView">AppView</td>
244244+ <td data-label="Retention" aria-label="Retention: Currently ephemeral; this policy will be updated if log retention is implemented">Currently ephemeral; this policy will be updated if log retention is implemented</td>
245245 </tr>
246246 <tr>
247247- <td data-label="Data Type">Layer records</td>
248248- <td data-label="Service">Hold PDS</td>
249249- <td data-label="Retention">Until you request deletion</td>
247247+ <td data-label="Data Type" aria-label="Data Type: Layer records">Layer records</td>
248248+ <td data-label="Service" aria-label="Service: Hold PDS">Hold PDS</td>
249249+ <td data-label="Retention" aria-label="Retention: Until you request deletion">Until you request deletion</td>
250250 </tr>
251251 <tr>
252252- <td data-label="Data Type">OCI blobs</td>
253253- <td data-label="Service">Hold Storage</td>
254254- <td data-label="Retention">Until no longer referenced (pruned within 30 days)</td>
252252+ <td data-label="Data Type" aria-label="Data Type: OCI blobs">OCI blobs</td>
253253+ <td data-label="Service" aria-label="Service: Hold Storage">Hold Storage</td>
254254+ <td data-label="Retention" aria-label="Retention: Until no longer referenced (pruned within 30 days)">Until no longer referenced (pruned within 30 days)</td>
255255 </tr>
256256 </tbody>
257257 </table>
···276276 <p>{{ .CompanyName }} supports "Bring Your Own Storage" where users can deploy their own hold services to store container image blobs. This section explains how BYOS affects your privacy rights.</p>
277277278278 <h3 class="text-lg font-medium mt-4">{{ .CompanyName }}-Hosted Holds</h3>
279279- <p>Hold services on <code class="bg-base-200 px-1.5 py-0.5 rounded text-sm font-mono">*.{{ .SiteURL }}</code> domains (e.g., <code class="bg-base-200 px-1.5 py-0.5 rounded text-sm font-mono">hold01.{{ .SiteURL }}</code>) are operated by us and fully covered by this privacy policy. We can fulfill all data access, export, and deletion requests for these services.</p>
279279+ <p>Hold services on <code class="bg-base-300 px-1.5 py-0.5 rounded text-sm font-mono">*.{{ .SiteURL }}</code> domains (e.g., <code class="bg-base-300 px-1.5 py-0.5 rounded text-sm font-mono">hold01.{{ .SiteURL }}</code>) are operated by us and fully covered by this privacy policy. We can fulfill all data access, export, and deletion requests for these services.</p>
280280281281 <h3 class="text-lg font-medium mt-6">User-Deployed Holds</h3>
282282 <p>If you use a hold service not operated by us:</p>
···303303 <p>Most data management can be done directly through your account settings at {{ .SiteURL }}:</p>
304304 <ul class="list-disc list-inside space-y-1 ml-4">
305305 <li><strong>Export your data:</strong> Use the "Export Data" button in settings to download a copy of all personal data we store about you.</li>
306306- <li><strong>Delete your data:</strong> Use the "Delete Account" button in settings. This will remove your layer records, cached data, and authentication tokens. You may also choose to have us delete <code class="bg-base-200 px-1.5 py-0.5 rounded text-sm font-mono">io.atcr.*</code> records from your PDS (requires active OAuth session).</li>
306306+ <li><strong>Delete your data:</strong> Use the "Delete Account" button in settings. This will remove your layer records, cached data, and authentication tokens. You may also choose to have us delete <code class="bg-base-300 px-1.5 py-0.5 rounded text-sm font-mono">io.atcr.*</code> records from your PDS (requires active OAuth session).</li>
307307 <li><strong>Revoke device tokens:</strong> Manage and revoke credential helper devices in settings.</li>
308308 <li><strong>Update your data:</strong> Corrections happen through normal use of the service.</li>
309309 </ul>
···334334 </div>
335335 </main>
336336337337- <div id="modal"></div>
338337339338 {{ template "footer" . }}
340339</body>
···88<body>
99 {{ template "nav" . }}
10101111- <main class="container mx-auto px-4 py-8 max-w-4xl">
1111+ <main id="main-content" class="container mx-auto px-4 py-8 max-w-4xl">
1212 <h1 class="text-3xl font-display font-bold tracking-tight mb-2">Terms of Service - {{ .CompanyName }} ({{ .SiteURL }})</h1>
1313 <p class="text-base-content/60 mb-8"><em>Last updated: January 2025</em></p>
1414···104104 <p>The Service operates on the AT Protocol, a distributed network. Data written to your PDS is controlled by you or your PDS hosting provider, not by us.</p>
105105106106 <h3 class="text-lg font-medium mt-6">7.2 Records on Your PDS</h3>
107107- <p>When you use the Service, records are written to your PDS under the <code class="bg-base-200 px-1.5 py-0.5 rounded text-sm font-mono">io.atcr.*</code> namespace. We can create, update, and delete these records only while you have granted us OAuth access. Revoking access does not automatically delete existing records.</p>
107107+ <p>When you use the Service, records are written to your PDS under the <code class="bg-base-300 px-1.5 py-0.5 rounded text-sm font-mono">io.atcr.*</code> namespace. We can create, update, and delete these records only while you have granted us OAuth access. Revoking access does not automatically delete existing records.</p>
108108109109 <h3 class="text-lg font-medium mt-6">7.3 No Control Over Your PDS</h3>
110110 <p>We do not control your PDS. If your PDS is offline or your PDS provider terminates your account, we cannot restore your AT Protocol records.</p>
···198198 </div>
199199 </main>
200200201201- <div id="modal"></div>
202201203202 {{ template "footer" . }}
204203</body>
···11{{ define "vulns-section" }}
22-<div class="card bg-base-100 shadow-sm border border-base-300 p-6 space-y-4 min-w-0">
22+<div class="space-y-4 min-w-0 pt-6">
33 {{ if .VulnData }}
44 {{ template "vuln-details" .VulnData }}
55 {{ else }}
66- <p class="text-base-content">No vulnerability scan data available</p>
66+ <div class="py-8 text-sm text-base-content/70 max-w-prose">
77+ <p class="font-medium text-base-content">No vulnerability scan available yet</p>
88+ <p class="mt-1">Scans run automatically shortly after a push. Check back in a few minutes, or push a new tag to trigger a scan.</p>
99+ </div>
710 {{ end }}
811</div>
912{{ end }}