Compare commits

...

8 Commits

8 changed files with 493 additions and 78 deletions

View File

@@ -1,10 +1,6 @@
# circlewithadot.net # 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 thats 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. 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 thats fine—contributions and ideas are always welcome.
- 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
--- ---
@@ -26,57 +22,3 @@ Static website for **[Circle With A Dot](https://circlewithadot.net)** — a sim
└─ about/ └─ about/
└─ javascript/ └─ javascript/
└─ index.html ← LibreJS Web Labels page └─ index.html ← LibreJS Web Labels page
---
## Components
### Click-to-copy
Use anywhere to get a “copied!” UX and copy to clipboard:
<span class="copyable" data-copy="https://example.com/inbox">
https://example.com/inbox
</span>
### 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/<slug>.html` (or start from a template).
2. Put the cover image in `images/` (e.g., `images/<slug>.png`).
3. Edit the page:
- Update `<title>`, 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 dont 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.

BIN
images/icon_gitea.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

View File

@@ -74,7 +74,7 @@
</div> </div>
<!-- Second row (smaller) --> <!-- Second row (smaller) -->
<div class="icon-row centralized-row smaller-row"> <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="" /> <img src="images/icon_youtube.png" alt="" />
</a> </a>
<a href="https://www.twitch.tv/incentivebeats" target="_blank" rel="noopener"> <a href="https://www.twitch.tv/incentivebeats" target="_blank" rel="noopener">
@@ -92,7 +92,13 @@
<a href="services/aprelay.html"> <a href="services/aprelay.html">
<img src="images/icon_aprelay.png" alt="" /> <img src="images/icon_aprelay.png" alt="" />
</a> </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="" /> <img src="images/icon_relibre.png" alt="" />
</a> </a>
</div> </div>
@@ -127,16 +133,13 @@
<a href="https://pixelfed.circlewithadot.net/@incentive" target="_blank" rel="noopener"> <a href="https://pixelfed.circlewithadot.net/@incentive" target="_blank" rel="noopener">
<img src="images/icon_pixelfed.png" alt="" /> <img src="images/icon_pixelfed.png" alt="" />
</a> </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"> <a href="https://blog.circlewithadot.net" target="_blank" rel="noopener">
<img src="images/icon_writefreely.png" alt="" /> <img src="images/icon_writefreely.png" alt="" />
</a> </a>
</div> </div>
<div class="icon-row centralized-row"> <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="" /> <img src="images/icon_bluesky.png" alt="" />
</a> </a>
<a href="http://facebook.com/incentivemusic" target="_blank" rel="noopener"> <a href="http://facebook.com/incentivemusic" target="_blank" rel="noopener">
@@ -145,10 +148,7 @@
<a href="http://instagram.com/incentivemusic" target="_blank" rel="noopener"> <a href="http://instagram.com/incentivemusic" target="_blank" rel="noopener">
<img src="images/icon_instagram.png" alt="" /> <img src="images/icon_instagram.png" alt="" />
</a> </a>
<a href="https://www.reddit.com/user/incentive_music/" target="_blank" rel="noopener"> <a href="https://www.threads.net/@incentivemusic" 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">
<img src="images/icon_threads.png" alt="" /> <img src="images/icon_threads.png" alt="" />
</a> </a>
<a href="https://x.com/incentivemusic" target="_blank" rel="noopener"> <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"> <a href="https://matrix.to/#/@incentive:matrix.circlewithadot.net" target="_blank" rel="noopener">
<img src="images/icon_matrix.png" alt="" /> <img src="images/icon_matrix.png" alt="" />
</a> </a>
<a href="https://mastodon.circlewithadot.net/@cwad" target="_blank" rel="me noopener">
<img src="images/icon_mastodon.png" alt="" />
</a>
</div> </div>
</section> </section>
</div> </div>

View File

@@ -6,7 +6,6 @@
<meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="viewport" content="width=device-width, initial-scale=1" />
<link href="../style.css?v=20250831" rel="stylesheet" /> <link href="../style.css?v=20250831" rel="stylesheet" />
<style> <style>
/* Relay-only CSS */
.relay-wrap { .relay-wrap {
max-width: 860px; max-width: 860px;
margin: 0 auto 60px; margin: 0 auto 60px;
@@ -30,7 +29,6 @@
font-size: 18px; font-size: 18px;
} }
/* logo next to header */
.relay-logo { .relay-logo {
display: block; display: block;
width: 80px; width: 80px;
@@ -87,6 +85,56 @@
color: #9aa0a6; color: #9aa0a6;
font-size: 14px; 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> </style>
<script src="aprelay.js" defer></script> <script src="aprelay.js" defer></script>
</head> </head>
@@ -97,6 +145,7 @@
<span class="brand-name">CIRCLE WITH A DOT</span> <span class="brand-name">CIRCLE WITH A DOT</span>
</a> </a>
</header> </header>
<main class="relay-wrap"> <main class="relay-wrap">
<header class="relay-header"> <header class="relay-header">
<img class="relay-logo" src="../images/aprelay_logo.png" alt="ActivityPub Relay logo" width="80" height="80"> <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 /> Last update: <span id="updated"></span><br />
<br /> <br />
Built using <a href="https://github.com/yukimochi/Activity-Relay" target="blank">Activity-Relay</a><br /> Built using <a href="https://github.com/yukimochi/Activity-Relay" target="blank">Activity-Relay</a><br />
<br > <br />
Only the above mentioned services are *officially* supported. Friendica and other instances may or may not work. Only the above mentioned services are <em>officially</em> supported. Friendica and other instances may or may not work.
</div> </div>
<noscript> <noscript>
<p class="stats"> <p class="stats">
JavaScript is disabled. Copy the relay URLs above. Live stats require JavaScript is disabled. Copy the relay URLs above. Live stats require JavaScript.
JavaScript.
</p> </p>
</noscript> </noscript>
</section> </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-inner">
<div class="footer-left"> <div class="footer-left">
<a <a
@@ -183,8 +290,8 @@
</footer> </footer>
</main> </main>
<a rel="me" href="https://mastodon.circlewithadot.net/@incentive" hidden> <a rel="me" href="https://mastodon.circlewithadot.net/@incentive" hidden>Mastodon</a>
Mastodon</a> <a rel="me" href="https://mastodon.circlewithadot.net/@cwad" hidden>Mastodon</a>
</body> </body>
</html> </html>

View File

@@ -64,3 +64,37 @@ async function loadStats() {
loadStats(); loadStats();
setInterval(loadStats, 300000); 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();
});

View File

@@ -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
}
]
}

155
services/hosting.html Normal file
View File

@@ -0,0 +1,155 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Hosting &amp; Administration — circlewithadot.net</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link href="../style.css?v=20250831" rel="stylesheet" />
<style>
/* Page-only CSS */
.page-wrap {
max-width: 860px;
margin: 0 auto 60px;
padding: 0 16px;
text-align: left;
}
.page-header {
display: flex;
align-items: center;
gap: 16px;
margin: 16px 0 10px;
}
.page-header h1 {
font-size: 44px;
margin: 0 0 4px;
}
.page-sub {
color: #9aa0a6;
margin: 0 0 18px;
font-size: 18px;
}
.page-card {
background: #121214;
border: 1px solid #1d1f21;
border-radius: 14px;
padding: 22px;
box-shadow: 0 14px 40px rgba(0,0,0,.45);
text-align: left;
}
.page-card p {
margin: 0;
line-height: 1.6;
}
.examples-title {
margin: 0 0 10px;
font-size: 16px;
color: #9aa0a6;
}
/* Make the icon row feel like the homepage “services” box */
.examples-card .icon-row {
margin-top: 8px;
justify-content: flex-start;
gap: 18px;
flex-wrap: wrap;
}
.examples-note {
margin-top: 12px;
color: #9aa0a6;
font-size: 14px;
line-height: 1.5;
}
</style>
</head>
<body>
<header class="site-header">
<a class="brand" href="../index.html">
<img class="brand-logo" src="../images/cwad_logo.png" alt="Circle With A Dot logo" />
<span class="brand-name">CIRCLE WITH A DOT</span>
</a>
</header>
<main class="page-wrap">
<header class="page-header">
<div>
<h1>Hosting &amp; Administration</h1>
<p class="page-sub">Limited availability • Case-by-case</p>
</div>
</header>
<section class="page-card">
<p>
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.
<br /><br />
Current offerings include Nextcloud, Matrix (Synapse), Mastodon, and blockchain nodes.
<br /><br />
Every deployment includes a hardened setup, monitoring, updates, and backend support (server, containers, and proxy),
with optional offsite backups.
<br /><br />
Email <a href="mailto:james@circlewithadot.net">james@circlewithadot.net</a> with a short description of your needs to
request an estimate and schedule a call.
</p>
</section>
<section class="page-card examples-card" style="margin-top:16px;">
<p class="examples-title"><strong>Examples of services we run</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>
<a href="https://peertube.circlewithadot.net" target="_blank" rel="noopener">
<img src="../images/icon_peertube.png" alt="PeerTube" />
</a>
<a href="https://funkwhale.circlewithadot.net" target="_blank" rel="noopener">
<img src="../images/icon_funkwhale.png" alt="Funkwhale" />
</a>
<a href="https://git.circlewithadot.net/explore/repos" target="_blank" rel="noopener">
<img src="../images/icon_gitea.png" alt="Gitea" />
</a>
</div>
<div class="examples-note">
Note: these are examples of platforms in our stack. Managed hosting offerings may vary by project.
</div>
</section>
<footer class="site-footer" role="contentinfo" style="margin-top:16px;">
<div class="footer-inner">
<div class="footer-left">
<a
href="https://creativecommons.org/licenses/by-nc-sa/4.0/"
target="_blank"
rel="noopener"
class="footer-text"
>
BY-NC-SA 4.0
</a>
</div>
<div class="footer-center">
<a
href="https://en.wikipedia.org/wiki/Copyleft"
target="_blank"
rel="noopener"
class="footer-icon"
aria-label="Copyleft (learn more)"
>
<img src="../images/cl.png" alt="Copyleft" />
</a>
</div>
<div class="footer-right footer-links">
<a href="../LICENSE">License</a>
</div>
</div>
</footer>
</main>
<a rel="me" href="https://mastodon.circlewithadot.net/@incentive" class="hidden">Mastodon</a>
</body>
</html>

View File

@@ -446,3 +446,56 @@ body {
justify-self: center; 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;
}