diff --git a/README.md b/README.md index 9f6b0da..ea04fbf 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,6 @@ # circlewithadot.net -Static website for **[Circle With A Dot](https://circlewithadot.net)** — a simple site for my music, tech projects, and socials. I keep the pages minimal so visitors can get where they want quickly. This site is tailored to my needs; feel free to fork and adapt it to yours (within the terms of the **AGPL-3.0-or-later**). Some services I add might not be relevant to you, and that’s fine—contributions and ideas are always welcome. I am not an HTML/CSS/JS expert, but I hope some of this is useful for you. - -- No build step; plain **HTML/CSS/JS** -- Works on any static host / reverse proxy (Caddy, Nginx, HAProxy, etc.) -- **LibreJS-friendly**: first-party JS is labeled and a Web Labels page is provided +Static website for **[Circle With A Dot](https://circlewithadot.net)** — a simple site for music, tech projects, and socials. I keep the pages minimal so visitors can get where they want quickly. This site is tailored to my needs; feel free to fork and adapt it to yours (within the terms of the **AGPL-3.0-or-later**). Some services I add might not be relevant to you, and that’s fine—contributions and ideas are always welcome. --- @@ -26,57 +22,3 @@ Static website for **[Circle With A Dot](https://circlewithadot.net)** — a sim └─ about/ └─ javascript/ └─ index.html ← LibreJS Web Labels page - ---- - -## Components - -### Click-to-copy - -Use anywhere to get a “copied!” UX and copy to clipboard: - - - https://example.com/inbox - - -### Relay stats widget (ActivityPub) - -`services/aprelay.js` tries these endpoints (in order) and fills `#instances`, `#jobs`, `#updated`: - -1. `https://relay-us-east.circlewithadot.net/relay-stats.json` -2. `/relay-stats.json` (same origin) - -Expected JSON: - - { "instances": 0, "jobs_per_min": 0, "updated": "2025-08-31T02:13:19Z" } - ---- - -## Release landing pages (Hyperfollow-style) - -Each release gets a simple landing page under `/releases/SLUG.html` that links out to Spotify/Apple/Bandcamp/PeerTube/etc. The release pages use `/releases/style.css`. - -### Add a new release - -1. Copy `releases/pessimist.html` to `releases/.html` (or start from a template). -2. Put the cover image in `images/` (e.g., `images/.png`). -3. Edit the page: - - Update ``, headings, description. - - Replace the cover image path + alt text. - - Paste platform links (clean URLs; no UTMs by default). -4. Optionally add a link on `index.html` pointing to the new release page. - -Notes: -- Icons live in `/images/icon_*.png`. Add more as needed and follow the same naming pattern. -- Use descriptive `alt` text and `aria-label`s for accessibility. - ---- - -## LibreJS & JavaScript licensing - -**LibreJS note:** All first-party JavaScript on this site is AGPL-3.0-or-later and labeled so the LibreJS add-on can verify and allow it. The Bandcamp embed on release pages loads third-party scripts that don’t publish LibreJS license markers, so LibreJS blocks that player by default (requiring whitelisting). I plan to replace the embed with a LibreJS-compatible approach (e.g., native `<audio>/<video>` with direct files) in a future update. - -## License - -- **Code** (HTML/CSS/JS): **AGPL-3.0-or-later** — see `LICENSE`. -- Media (art, audio) may have different licenses; see file-level notes where applicable. diff --git a/images/icon_gitea.png b/images/icon_gitea.png new file mode 100644 index 0000000..8b84570 Binary files /dev/null and b/images/icon_gitea.png differ diff --git a/index.html b/index.html index 2819b1a..cf2ebd8 100644 --- a/index.html +++ b/index.html @@ -74,7 +74,7 @@ </div> <!-- Second row (smaller) --> <div class="icon-row centralized-row smaller-row"> - <a href="https://youtube.com/@incentive_music" target="_blank" rel="noopener"> + <a href="https://youtube.com/@incentive_music" target_hint="_blank" rel="noopener"> <img src="images/icon_youtube.png" alt="" /> </a> <a href="https://www.twitch.tv/incentivebeats" target="_blank" rel="noopener"> @@ -92,7 +92,13 @@ <a href="services/aprelay.html"> <img src="images/icon_aprelay.png" alt="" /> </a> - <a href="https://relibre.site"> + + <!-- NEW: hosting/services page --> + <a href="services/hosting.html"> + <img src="images/cwad_logo.png" alt="Hosting & Administration" /> + </a> + + <a href="https://circlewithadot.net/relibre"> <img src="images/icon_relibre.png" alt="" /> </a> </div> @@ -127,16 +133,13 @@ <a href="https://pixelfed.circlewithadot.net/@incentive" target="_blank" rel="noopener"> <img src="images/icon_pixelfed.png" alt="" /> </a> - <a href="https://lemmy.ml/u/incentive" target="_blank" rel="noopener"> - <img src="images/icon_lemmy.png" alt="" /> - </a> <a href="https://blog.circlewithadot.net" target="_blank" rel="noopener"> <img src="images/icon_writefreely.png" alt="" /> </a> </div> <div class="icon-row centralized-row"> - <a href="https://bsky.app/profile/did:plc:yulwemd4he5qhnzmtcbgzo5d" target="_blank" rel="noopener"> + <a href="https://bsky.app/profile/incentive.bsky.social" target="_blank" rel="noopener"> <img src="images/icon_bluesky.png" alt="" /> </a> <a href="http://facebook.com/incentivemusic" target="_blank" rel="noopener"> @@ -145,10 +148,7 @@ <a href="http://instagram.com/incentivemusic" target="_blank" rel="noopener"> <img src="images/icon_instagram.png" alt="" /> </a> - <a href="https://www.reddit.com/user/incentive_music/" target="_blank" rel="noopener"> - <img src="images/icon_reddit.png" alt="" /> - </a> - <a href="https://www.threads.com/fediverse_profile/incentive@mastodon.circlewithadot.net" target="_blank" rel="noopener"> + <a href="https://www.threads.net/@incentivemusic" target="_blank" rel="noopener"> <img src="images/icon_threads.png" alt="" /> </a> <a href="https://x.com/incentivemusic" target="_blank" rel="noopener"> @@ -167,6 +167,9 @@ <a href="https://matrix.to/#/@incentive:matrix.circlewithadot.net" target="_blank" rel="noopener"> <img src="images/icon_matrix.png" alt="" /> </a> + <a href="https://mastodon.circlewithadot.net/@cwad" target="_blank" rel="me noopener"> + <img src="images/icon_mastodon.png" alt="" /> + </a> </div> </section> </div> diff --git a/services/aprelay.html b/services/aprelay.html index 5fdb140..447707a 100644 --- a/services/aprelay.html +++ b/services/aprelay.html @@ -6,7 +6,6 @@ <meta name="viewport" content="width=device-width, initial-scale=1" /> <link href="../style.css?v=20250831" rel="stylesheet" /> <style> - /* Relay-only CSS */ .relay-wrap { max-width: 860px; margin: 0 auto 60px; @@ -30,7 +29,6 @@ font-size: 18px; } - /* logo next to header */ .relay-logo { display: block; width: 80px; @@ -87,6 +85,56 @@ color: #9aa0a6; font-size: 14px; } + + .updates-title { + margin: 0 0 10px; + font-size: 16px; + color: #9aa0a6; + } + .updates-card .icon-row { + margin-top: 8px; + justify-content: flex-start; + gap: 18px; + flex-wrap: wrap; + } + .updates-note { + margin-top: 12px; + color: #9aa0a6; + font-size: 14px; + line-height: 1.5; + } + + .policy-card { + font-size: 14px; + } + .policy-title { + margin: 0 0 10px; + font-size: 16px; + color: #9aa0a6; + } + .policy-card p { + margin: 0; + color: #e6e6e6; + line-height: 1.6; + font-size: 14px; + } + .policy-card ul { + margin: 10px 0 0 20px; + padding: 0; + color: #e6e6e6; + line-height: 1.6; + font-size: 14px; + } + .policy-card li { + margin: 6px 0; + font-size: 14px; + } + .policy-note { + margin-top: 12px; + color: #9aa0a6; + font-size: 14px; + line-height: 1.5; + } </style> <script src="aprelay.js" defer></script> </head> @@ -97,6 +145,7 @@ <span class="brand-name">CIRCLE WITH A DOT</span> </a> </header> + <main class="relay-wrap"> <header class="relay-header"> <img class="relay-logo" src="../images/aprelay_logo.png" alt="ActivityPub Relay logo" width="80" height="80"> @@ -139,19 +188,77 @@ Last update: <span id="updated">–</span><br /> <br /> Built using <a href="https://github.com/yukimochi/Activity-Relay" target="blank">Activity-Relay</a><br /> - <br > - Only the above mentioned services are *officially* supported. Friendica and other instances may or may not work. + <br /> + Only the above mentioned services are <em>officially</em> supported. Friendica and other instances may or may not work. </div> <noscript> <p class="stats"> - JavaScript is disabled. Copy the relay URLs above. Live stats require - JavaScript. + JavaScript is disabled. Copy the relay URLs above. Live stats require JavaScript. </p> </noscript> </section> - <footer class="site-footer" role="contentinfo"> + <section class="relay-card updates-card" style="margin-top:16px;"> + <p class="updates-title"><strong>For relay updates, check here</strong></p> + <div class="icon-row"> + <a href="https://mastodon.circlewithadot.net/@cwad" target="_blank" rel="noopener"> + <img src="../images/icon_mastodon.png" alt="Mastodon" /> + </a> + </div> + <div class="updates-note"> + Follow <a href="https://mastodon.circlewithadot.net/@cwad" target="_blank" rel="noopener">@cwad</a> for maintenance notices and relay-related updates. + </div> + </section> + + <section class="relay-card policy-card" style="margin-top:16px;"> + <p class="policy-title"><strong>Relay policy</strong></p> + + <p> + This relay follows the spirit of the + <a href="https://joinmastodon.org/covenant" target="_blank" rel="noopener">Mastodon Server Covenant</a>. + We aim to keep the relay safe, respectful, and usable for the broader Fediverse. + </p> + + <ul> + <li> + We will remove any servers from the relay known to actively promote homophobia, transphobia, racism, + or any other form of bigotry. + </li> + <li> + We expect participating servers to be at minimum <strong>well maintained</strong> (e.g., reasonable upkeep, + responsive administration, and not persistently broken or abusive to federation). + </li> + </ul> + + <div class="policy-note"> + If a server on the relay becomes a problem, please reach out via the updates account above. + </div> + </section> + + <section class="relay-card instances-card" style="margin-top:16px;"> + <p class="instances-title"><strong>Connected instances</strong></p> + + <div class="instances-table" role="table" aria-label="Connected relay instances"> + <div class="instances-header" role="row"> + <span role="columnheader">Instance</span> + <span role="columnheader">Status</span> + </div> + + <div id="instances-body" class="instances-body" role="rowgroup"> + <div class="instances-row" role="row"> + <span class="mono" role="cell">Loading…</span> + <span role="cell">–</span> + </div> + </div> + </div> + + <div class="instances-meta"> + Last check: <span id="instances-updated">–</span> + </div> + </section> + + <footer class="site-footer" role="contentinfo" style="margin-top:16px;"> <div class="footer-inner"> <div class="footer-left"> <a @@ -183,8 +290,8 @@ </footer> </main> - <a rel="me" href="https://mastodon.circlewithadot.net/@incentive" hidden> - Mastodon</a> + <a rel="me" href="https://mastodon.circlewithadot.net/@incentive" hidden>Mastodon</a> + <a rel="me" href="https://mastodon.circlewithadot.net/@cwad" hidden>Mastodon</a> </body> </html> diff --git a/services/aprelay.js b/services/aprelay.js index a5595f5..ae487b1 100644 --- a/services/aprelay.js +++ b/services/aprelay.js @@ -64,3 +64,37 @@ async function loadStats() { loadStats(); setInterval(loadStats, 300000); +async function loadInstanceStatuses() { + const bodyEl = document.getElementById("instances-body"); + const updatedEl = document.getElementById("instances-updated"); + if (!bodyEl || !updatedEl) return; + + try { + const res = await fetch("aprelay_instances.json", { cache: "no-store" }); + if (!res.ok) throw new Error(`HTTP ${res.status}`); + + const data = await res.json(); + const instances = Array.isArray(data.instances) ? data.instances : []; + updatedEl.textContent = data.generated_at || "–"; + + if (!instances.length) { + bodyEl.innerHTML = `<div class="instances-row"><span class="mono">No data</span><span>–</span></div>`; + return; + } + + bodyEl.innerHTML = instances.map(x => ` + <div class="instances-row" role="row"> + <span class="mono" role="cell">${x.domain}</span> + <span role="cell">${x.status}</span> + </div> + `).join(""); + } catch (e) { + bodyEl.innerHTML = `<div class="instances-row"><span class="mono">Error loading list</span><span>–</span></div>`; + updatedEl.textContent = "–"; + } +} + +document.addEventListener("DOMContentLoaded", () => { + loadInstanceStatuses(); +}); + diff --git a/services/aprelay_instances.json b/services/aprelay_instances.json new file mode 100644 index 0000000..560cb53 --- /dev/null +++ b/services/aprelay_instances.json @@ -0,0 +1,121 @@ +{ + "generated_at": "2026-02-07 02:03:58 UTC", + "instances": [ + { + "domain": "activitypub.space", + "status": "working", + "code": 200 + }, + { + "domain": "ailbhean.co-shaoghal.net", + "status": "working", + "code": 200 + }, + { + "domain": "bitforged.social", + "status": "dead", + "code": null + }, + { + "domain": "clar.ke", + "status": "working", + "code": 200 + }, + { + "domain": "cyangant.dedyn.io", + "status": "dead", + "code": null + }, + { + "domain": "declin.eu", + "status": "working", + "code": 200 + }, + { + "domain": "fed.fsub.de", + "status": "working", + "code": 200 + }, + { + "domain": "fedifreu.de", + "status": "working", + "code": 200 + }, + { + "domain": "fleacf.space", + "status": "working", + "code": 200 + }, + { + "domain": "irlqt.net", + "status": "working", + "code": 200 + }, + { + "domain": "kosen-agola-misskey.kouta2133.work", + "status": "dead", + "code": null + }, + { + "domain": "mammut.gogreenit.net", + "status": "working", + "code": 200 + }, + { + "domain": "mastodon.circlewithadot.net", + "status": "working", + "code": 200 + }, + { + "domain": "misskey.jayhsustudio.com", + "status": "530", + "code": 530 + }, + { + "domain": "misskey.xstudiotw.net", + "status": "working", + "code": 200 + }, + { + "domain": "mitchelltribe.rodeo", + "status": "working", + "code": 200 + }, + { + "domain": "mstdn.canarylabs.eu", + "status": "working", + "code": 200 + }, + { + "domain": "mstdn.catbyte.tech", + "status": "dead", + "code": null + }, + { + "domain": "mstdn.fan", + "status": "dead", + "code": null + }, + { + "domain": "okpeace.org", + "status": "dead", + "code": null + }, + { + "domain": "pl.aelaraji.com", + "status": "working", + "code": 200 + }, + { + "domain": "relay.fedi.buzz", + "status": "working", + "code": 200 + }, + { + "domain": "social.domaincrawler.com", + "status": "working", + "code": 200 + } + ] +} + diff --git a/services/hosting.html b/services/hosting.html new file mode 100644 index 0000000..b467ed7 --- /dev/null +++ b/services/hosting.html @@ -0,0 +1,155 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8" /> + <title>Hosting & Administration — circlewithadot.net + + + + + + + +
+ + +
+

+ We provide limited-availability, case-by-case managed hosting and administration for organizations that want reliable + services without running the infrastructure in-house. We do not offer web design services. +

+ Current offerings include Nextcloud, Matrix (Synapse), Mastodon, and blockchain nodes. +

+ Every deployment includes a hardened setup, monitoring, updates, and backend support (server, containers, and proxy), + with optional offsite backups. +

+ Email james@circlewithadot.net with a short description of your needs to + request an estimate and schedule a call. +

+
+ +
+

Examples of services we run

+ +
+ Note: these are examples of platforms in our stack. Managed hosting offerings may vary by project. +
+
+ + +
+ + + + \ No newline at end of file diff --git a/style.css b/style.css index 1ec48b1..a29575d 100644 --- a/style.css +++ b/style.css @@ -446,3 +446,56 @@ body { justify-self: center; } } + +.instances-title { + margin: 0 0 10px; + font-size: 16px; + color: #9aa0a6; +} + +.instances-table { + border: 1px solid #26282a; + border-radius: 12px; + overflow: hidden; + background: #0f0f10; +} + +.instances-header { + display: grid; + grid-template-columns: 1fr 140px; + gap: 12px; + padding: 10px 12px; + border-bottom: 1px solid #26282a; + color: #9aa0a6; + font-size: 14px; +} + +.instances-body { + max-height: 240px; /* scrolling area */ + overflow: auto; +} + +.instances-row { + display: grid; + grid-template-columns: 1fr 140px; + gap: 12px; + padding: 10px 12px; + border-bottom: 1px solid #1f2123; + font-size: 14px; +} + +.instances-row:last-child { + border-bottom: 0; +} + +.mono { + font-family: ui-monospace, SFMono-Regular, Consolas, monospace; + word-break: break-all; +} + +.instances-meta { + margin-top: 10px; + color: #9aa0a6; + font-size: 14px; +} +