Skip to content

Instantly share code, notes, and snippets.

@examosa
Last active September 26, 2025 16:57
Show Gist options
  • Save examosa/50eb28bc16a006b62b3f43893ab457e7 to your computer and use it in GitHub Desktop.
Save examosa/50eb28bc16a006b62b3f43893ab457e7 to your computer and use it in GitHub Desktop.

Revisions

  1. examosa revised this gist Sep 26, 2025. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion shortside.user.js
    Original file line number Diff line number Diff line change
    @@ -1,6 +1,6 @@
    // ==UserScript==
    // @name Shortside
    // @version 1.4.4
    // @version 1.4.5
    // @description Redirects the configured websites to third-party privacy-respecting front ends.
    // @author examosa
    // @license AGPLv3-or-later
  2. examosa revised this gist Sep 26, 2025. 1 changed file with 0 additions and 1 deletion.
    1 change: 0 additions & 1 deletion shortside.user.js
    Original file line number Diff line number Diff line change
    @@ -51,7 +51,6 @@ function getRedirect() {
    'https://anonoverflow.hyperreal.coffee',
    'https://overflow.r4fo.com',
    'https://overflow.ducks.party',
    'https://overflow.snine.nl',
    'https://anonymousoverflow.privacyredirect.com',
    'https://soflow.nerdvpn.de',
    'https://overflow.einfachzocken.eu',
  3. examosa revised this gist Sep 24, 2025. 2 changed files with 68 additions and 49 deletions.
    104 changes: 62 additions & 42 deletions fetch-instances.js
    Original file line number Diff line number Diff line change
    @@ -1,44 +1,36 @@
    const cdn = (path) => new URL(path, 'https://nobsdelivr.private.coffee/');
    const gh = (path) => new URL(path, 'https://nobsdelivr.private.coffee/gh');

    const withCatch =
    (action) =>
    (...args) =>
    Promise.try(action, ...args).catch((error) => {
    console.error('Caught error:', error);
    const getJson = (url) =>
    fetch(url, { headers: { Accept: "application/json" } })
    .then((response) => response.json())
    .catch((error) => {
    console.error(error);
    });

    const getJson = withCatch((url) =>
    fetch(url, { headers: { Accept: 'application/json' } }).then((response) =>
    response.json(),
    ),
    );

    async function testBackups() {
    const backups = [
    'https://jsonblob.com/api/jsonBlob/1392945386074857472',
    'https://api.npoint.io/6ee2cb1b5a1aa6f10be5',
    'https://api.pastes.dev/VSjbFP63yq',
    'https://api.codetabs.com/v1/proxy/?quest=https%3A%2F%2Fstarb.in%2Fraw%2FMgDjym',
    'https://bytebin.lucko.me/bDAxqrUNqD',
    async function fetchInstances() {
    const targetTypes = [
    'anonymousoverflow',
    'breezewiki',
    'dumb',
    'eddrit',
    'intellectual',
    'invidious',
    'libmedium',
    'nerdsfornerds',
    'neuters',
    'nitter',
    'photon-reddit',
    'priviblur',
    'quetre',
    'redlib',
    'ruraldictionary',
    'scribe',
    ];

    const results = await Promise.all(backups.map((backup) => getJson(backup)));

    for (const index of results.keys()) {
    const result = results[index];

    if (!result) {
    const backup = backups[index];
    console.warn(`Stale backup: ${backup}`);
    }
    }
    }

    async function fetchInstances() {
    const instances = {
    ducksforducks: ['https://ducksforducks.private.coffee'],
    nerdsfornerds: ['https://nn.vern.cc'],
    };
    const instances = targetTypes.reduce((out, type) => {
    out[type] = [];
    return out;
    }, Object.create(null));

    const providers = {
    libredirect: {
    @@ -55,7 +47,9 @@ async function fetchInstances() {
    normalize: (result) =>
    result.map((service) => ({
    type: service.type,
    instances: service.instances.concat(service.fallback),
    instances: service.instances
    .filter((instance) => !instance.endsWith('.onion'))
    .concat(service.fallback),
    })),
    },

    @@ -72,7 +66,7 @@ async function fetchInstances() {
    };

    const promises = Object.values(providers).map(async (provider) => {
    const result = await getJson(cdn(`gh/${provider.source}`));
    const result = await getJson(gh(provider.source));

    if (!result) {
    return [];
    @@ -84,14 +78,40 @@ async function fetchInstances() {
    const sources = await Promise.all(promises);

    for (const source of sources.flat()) {
    const list = (instances[source.type] ??= []);
    if (!(source.type in instances)) continue;

    for (const url of source.instances) {
    if (!list.includes(url)) {
    list.push(url);
    const list = instances[source.type];

    for (const instance of source.instances) {
    const { href } = new URL(instance);

    if (!list.includes(href)) {
    list.push(href);
    list.sort();
    }
    }
    }

    return instances;
    }

    async function testBackups() {
    const backups = [
    'https://jsonblob.com/api/jsonBlob/1392945386074857472',
    'https://api.npoint.io/6ee2cb1b5a1aa6f10be5',
    'https://api.pastes.dev/VSjbFP63yq',
    'https://corsmirror.com/v1?url=https%3A%2F%2Fstarb.in%2Fraw%2FMgDjym',
    'https://bytebin.lucko.me/bDAxqrUNqD',
    ];

    const results = await Promise.all(backups.map((backup) => getJson(backup)));

    for (const index of results.keys()) {
    const result = results[index];

    if (!result) {
    const backup = backups[index];
    console.warn(`Stale backup: ${backup}`);
    }
    }
    }
    13 changes: 6 additions & 7 deletions shortside.user.js
    Original file line number Diff line number Diff line change
    @@ -1,12 +1,13 @@
    // ==UserScript==
    // @name Shortside
    // @version 1.4.3
    // @version 1.4.4
    // @description Redirects the configured websites to third-party privacy-respecting front ends.
    // @author examosa
    // @license AGPLv3-or-later
    // @homepageUrl https://gist.github.com/examosa/50eb28bc16a006b62b3f43893ab457e7/
    // @updateUrl https://gist.github.com/examosa/50eb28bc16a006b62b3f43893ab457e7/raw/shortside.user.js
    // @downloadUrl https://gist.github.com/examosa/50eb28bc16a006b62b3f43893ab457e7/raw/shortside.user.js
    // @homepageURL https://gist.github.com/examosa/50eb28bc16a006b62b3f43893ab457e7/
    // @updateURL https://gist.github.com/examosa/50eb28bc16a006b62b3f43893ab457e7/raw/shortside.user.js
    // @downloadURL https://gist.github.com/examosa/50eb28bc16a006b62b3f43893ab457e7/raw/shortside.user.js
    // @icon https://nobsdelivr.private.coffee/gh/webalys-hq/streamline-vectors/plump/gradient/interface-essential/shuffle.svg
    // @match https://www.fandom.com/*
    // @match https://www.geeksforgeeks.org/*
    // @match https://genius.com/*
    @@ -22,7 +23,7 @@
    // @match https://twitter.com/*
    // @match https://www.urbandictionary.com/*
    // @match https://x.com/*
    // @run-at document_start
    // @run-at document-start
    // @allFrames false
    // @noframes
    // ==/UserScript==
    @@ -44,8 +45,6 @@ function getRedirect() {
    'https://overflow.lunar.icu',
    'https://overflow.adminforge.de',
    'https://overflow.hostux.net',
    'https://overflow.projectsegfau.lt',
    'https://overflow.fascinated.cc',
    'https://ao.bloat.cat',
    'https://ao.owo.si',
    'https://overflow.freedit.eu',
  4. examosa revised this gist Sep 2, 2025. 1 changed file with 2 additions and 1 deletion.
    3 changes: 2 additions & 1 deletion shortside.user.js
    Original file line number Diff line number Diff line change
    @@ -1,9 +1,10 @@
    // ==UserScript==
    // @name Shortside
    // @version 1.4.2
    // @version 1.4.3
    // @description Redirects the configured websites to third-party privacy-respecting front ends.
    // @author examosa
    // @license AGPLv3-or-later
    // @homepageUrl https://gist.github.com/examosa/50eb28bc16a006b62b3f43893ab457e7/
    // @updateUrl https://gist.github.com/examosa/50eb28bc16a006b62b3f43893ab457e7/raw/shortside.user.js
    // @downloadUrl https://gist.github.com/examosa/50eb28bc16a006b62b3f43893ab457e7/raw/shortside.user.js
    // @match https://www.fandom.com/*
  5. examosa revised this gist Aug 25, 2025. 2 changed files with 42 additions and 29 deletions.
    5 changes: 1 addition & 4 deletions fetch-instances.js
    Original file line number Diff line number Diff line change
    @@ -94,7 +94,4 @@ async function fetchInstances() {
    }

    return instances;
    }

    await testBackups();
    await fetchInstances();
    }
    66 changes: 41 additions & 25 deletions shortside.user.js
    Original file line number Diff line number Diff line change
    @@ -1,6 +1,6 @@
    // ==UserScript==
    // @name Shortside
    // @version 1.4.1
    // @version 1.4.2
    // @description Redirects the configured websites to third-party privacy-respecting front ends.
    // @author examosa
    // @license AGPLv3-or-later
    @@ -9,6 +9,7 @@
    // @match https://www.fandom.com/*
    // @match https://www.geeksforgeeks.org/*
    // @match https://genius.com/*
    // @match https://www.instagram.com/*
    // @match https://www.medium.com/*
    // @match https://medium.com/*
    // @match https://stackoverflow.com/*
    @@ -29,14 +30,11 @@ stop();

    function getRedirect() {
    const hashPath = (...urls) =>
    urls.map(
    (href) => (url) =>
    Object.assign(url, {
    href,
    hash: url.pathname.slice(1),
    search: '',
    }),
    );
    urls.map((href) => (url) => {
    const { pathname } = url;
    url.href = href;
    url.hash = pathname.slice(1);
    });

    const instances = {
    anonymousoverflow: [
    @@ -103,6 +101,13 @@ function getRedirect() {
    'https://dumb.lunar.icu',
    ],
    eddrit: ['https://eddrit.com'],
    imginn: [
    function imginn(url) {
    const { pathname } = url;
    url.href = 'https://imginn.com';
    url.pathname = pathname.replace('/reel/', '/p/');
    },
    ],
    intellectual: [
    'https://in.bloat.cat',
    'https://intellectual.catsarch.com',
    @@ -174,32 +179,29 @@ function getRedirect() {
    function rdx(url) {
    const path = url.pathname;

    const userPattern = /^\/u(?:ser)?\/([^/]+)/;
    const subPattern = /^\/r\/([^/]+)/;
    const user = path.replace(/^\/u(?:ser)?\/([^/]+)/, '$1');
    const subreddit = path.replace(/^\/r\/([^/]+)/, '$1');

    if (path.includes('/comments/')) {
    url.searchParams.set('url', url.href);
    url.pathname = '/comments.html';
    } else if (userPattern.test(path)) {
    const matches = path.match(userPattern);
    url.searchParams.set('u', matches[1]);
    } else if (user !== path) {
    url.searchParams.set('u', user);
    url.pathname = '/user.html';
    } else if (subPattern.test(path)) {
    const matches = path.match(subPattern);
    url.searchParams.set('r', matches[1]);
    } else if (subreddit !== path) {
    url.searchParams.set('r', subreddit);
    url.pathname = '/subreddit.html';
    }

    url.hostname = 'rdx.overdevs.com';

    return url;
    },
    ],
    reditr: ['https://reditr.com/'],
    redlib: [
    'https://redlib.catsarch.com',
    'https://redlib.perennialte.ch',
    'https://redlib.privacyredirect.com',
    'https://redlib.private.coffee',
    'https://reddit.nerdvpn.de',
    'https://reddit.adminforge.de',
    'https://rl.blitzw.in',
    @@ -232,12 +234,20 @@ function getRedirect() {
    'https://scribe.rip',
    ],
    small: ['https://small.private.coffee'],
    skipvids: [
    function skipVids(url) {
    const { href } = url;
    url.href = 'https://skipvids.com';
    url.searchParams.set('search_query', href);
    },
    ],
    };

    const replacements = Object.entries({
    fandom: ['breezewiki'],
    geeksforgeeks: ['ducksforducks', 'nerdsfornerds'],
    genius: ['dumb', 'intellectual'],
    instagram: ['imginn'],
    medium: ['libmedium', 'scribe', 'small'],
    quora: ['quetre'],
    reddit: [
    @@ -256,11 +266,13 @@ function getRedirect() {
    twitter: ['nitter'],
    urbandictionary: ['ruraldictionary'],
    'x.com': ['nitter'],
    youtube: ['invidious'],
    youtube: ['invidious', 'skipvids'],
    });

    const redirect = new URL(location);

    const replacement = replacements.find(([host]) =>
    location.hostname.includes(host),
    redirect.hostname.includes(host),
    );

    if (replacement) {
    @@ -276,15 +288,19 @@ function getRedirect() {
    if (instancesForType?.length > 0) {
    const instance = chooseRandom(instancesForType);

    const redirect = isFunction(instance)
    ? instance(new URL(location))
    : new URL(location.pathname, instance);
    if (isFunction(instance)) {
    instance(redirect);
    } else {
    redirect.href = instance;
    redirect.pathname = location.pathname;
    }

    return redirect;
    }
    }

    const redirect = new URL(`/_/${location.href}`, 'https://farside.link');
    redirect.href = 'https://farside.link';
    redirect.pathname = `/_/${location}`;

    return redirect;
    }
  6. examosa revised this gist Aug 21, 2025. 1 changed file with 3 additions and 1 deletion.
    4 changes: 3 additions & 1 deletion shortside.user.js
    Original file line number Diff line number Diff line change
    @@ -1,6 +1,6 @@
    // ==UserScript==
    // @name Shortside
    // @version 1.4.0
    // @version 1.4.1
    // @description Redirects the configured websites to third-party privacy-respecting front ends.
    // @author examosa
    // @license AGPLv3-or-later
    @@ -25,6 +25,8 @@
    // @noframes
    // ==/UserScript==

    stop();

    function getRedirect() {
    const hashPath = (...urls) =>
    urls.map(
  7. examosa revised this gist Aug 19, 2025. 1 changed file with 208 additions and 154 deletions.
    362 changes: 208 additions & 154 deletions shortside.user.js
    Original file line number Diff line number Diff line change
    @@ -1,6 +1,6 @@
    // ==UserScript==
    // @name Shortside
    // @version 1.3.2
    // @version 1.4.0
    // @description Redirects the configured websites to third-party privacy-respecting front ends.
    // @author examosa
    // @license AGPLv3-or-later
    @@ -26,168 +26,210 @@
    // ==/UserScript==

    function getRedirect() {
    const hashPath = (...urls) =>
    urls.map(
    (href) => (url) =>
    Object.assign(url, {
    href,
    hash: url.pathname.slice(1),
    search: '',
    }),
    );

    const instances = {
    ducksforducks: ["https://ducksforducks.private.coffee"],
    nerdsfornerds: ["https://nn.vern.cc"],
    invidious: [
    "https://inv.nadeko.net",
    "https://invidious.nerdvpn.de",
    "https://invidious.f5.si",
    "https://yewtu.be",
    "https://invidious.snopyta.org",
    anonymousoverflow: [
    'https://code.whatever.social',
    'https://ao.vern.cc',
    'https://overflow.lunar.icu',
    'https://overflow.adminforge.de',
    'https://overflow.hostux.net',
    'https://overflow.projectsegfau.lt',
    'https://overflow.fascinated.cc',
    'https://ao.bloat.cat',
    'https://ao.owo.si',
    'https://overflow.freedit.eu',
    'https://anonoverflow.hyperreal.coffee',
    'https://overflow.r4fo.com',
    'https://overflow.ducks.party',
    'https://overflow.snine.nl',
    'https://anonymousoverflow.privacyredirect.com',
    'https://soflow.nerdvpn.de',
    'https://overflow.einfachzocken.eu',
    'https://overflow.seasi.dev',
    'https://anonymousoverflow.catsarch.com',
    'https://overflow.darkness.services',
    'https://anonflow.aketawi.space',
    'https://overflow.canine.tools',
    'https://ao.bloat.cat',
    ],
    nitter: [
    "https://xcancel.com",
    "https://nitter.privacyredirect.com",
    "https://lightbrd.com",
    "https://nitter.space",
    "https://nitter.tiekoetter.com",
    "https://nuku.trabun.org",
    "https://nitter.net",
    breezewiki: [
    'https://breezewiki.com',
    'https://antifandom.com',
    'https://breezewiki.pussthecat.org',
    'https://bw.projectsegfau.lt',
    'https://breeze.hostux.net',
    'https://bw.artemislena.eu',
    'https://breeze.nohost.network',
    'https://breeze.whateveritworks.org',
    'https://z.opnxng.com',
    'https://breezewiki.hyperreal.coffee',
    'https://breezewiki.catsarch.com',
    'https://breeze.mint.lgbt',
    'https://breezewiki.nadeko.net',
    'https://fandom.reallyaweso.me',
    'https://breezewiki.r4fo.com',
    'https://breezewiki.private.coffee',
    'https://fan.blitzw.in',
    'https://antifandom.com',
    'https://breezewiki.com',
    'https://breezewiki.nadeko.net',
    'https://breezewiki.pussthecat.org',
    'https://bw.projectsegfau.lt',
    ],
    redlib: [
    "https://redlib.catsarch.com",
    "https://redlib.perennialte.ch",
    "https://redlib.privacyredirect.com",
    "https://reddit.nerdvpn.de",
    "https://reddit.adminforge.de",
    "https://rl.blitzw.in",
    "https://redlib.orangenet.cc",
    "https://redlib.privadency.com",
    "https://l.opnxng.com",
    "https://red.artemislena.eu",
    "https://redlib.nadeko.net",
    "https://rl.bloat.cat",
    "https://safereddit.com",
    "https://redlib.freedit.eu",
    ducksforducks: ['https://ducksforducks.private.coffee'],
    dumb: [
    'https://dm.vern.cc',
    'https://dumb.lunar.icu',
    'https://dumb.ducks.party',
    'https://dumb.hyperreal.coffee',
    'https://dumb.bloat.cat',
    'https://dumb.jeikobu.net',
    'https://dumb.canine.tools',
    'https://lyr.dc09.ru',
    'https://db.kuuro.net',
    'https://dumb.ducks.party',
    'https://dumb.lunar.icu',
    ],
    scribe: [
    "https://scribe.rip",
    "https://scribe.nixnet.services",
    "https://scribe.citizen4.eu",
    "https://scribe.froth.zone",
    "https://scribe.rawbit.ninja",
    "https://sc.vern.cc",
    "https://scribe.r4fo.com",
    "https://scribe.privacyredirect.com",
    "https://scribe.privacyredirect.com/",
    "https://scribe.rawbit.ninja/",
    "https://scribe.rip/",
    eddrit: ['https://eddrit.com'],
    intellectual: [
    'https://in.bloat.cat',
    'https://intellectual.catsarch.com',
    'https://intellectual.insprill.net',
    ],
    small: ["https://small.private.coffee"],
    quetre: [
    "https://quetre.iket.me",
    "https://qr.vern.cc",
    "https://quetre.pussthecat.org",
    "https://ask.habedieeh.re",
    "https://quetre.blackdrgn.nl",
    "https://quetre.lunar.icu",
    "https://quetre.drgns.space",
    "https://quetre.r4fo.com",
    "https://quetre.ducks.party",
    "https://quetre.private.coffee",
    "https://quetre.canine.tools",
    "https://qt.bloat.cat",
    "https://quetre.jeikobu.net",
    "https://q.307200.xyz",
    "https://quora.nerdvpn.de",
    "https://quetre.privacyredirect.com",
    "https://quora.vern.cc",
    "https://quetre.pufe.org",
    "https://questions.whateveritworks.org",
    "https://quetre.odyssey346.dev/",
    "https://quetre.pussthecat.org/",
    invidious: [
    'https://inv.nadeko.net',
    'https://invidious.nerdvpn.de',
    'https://invidious.f5.si',
    'https://yewtu.be',
    'https://invidious.snopyta.org',
    ],
    breezewiki: [
    "https://breezewiki.com",
    "https://antifandom.com",
    "https://breezewiki.pussthecat.org",
    "https://bw.projectsegfau.lt",
    "https://breeze.hostux.net",
    "https://bw.artemislena.eu",
    "https://breeze.nohost.network",
    "https://breeze.whateveritworks.org",
    "https://z.opnxng.com",
    "https://breezewiki.hyperreal.coffee",
    "https://breezewiki.catsarch.com",
    "https://breeze.mint.lgbt",
    "https://breezewiki.nadeko.net",
    "https://fandom.reallyaweso.me",
    "https://breezewiki.r4fo.com",
    "https://breezewiki.private.coffee",
    "https://fan.blitzw.in",
    "https://antifandom.com/",
    "https://breezewiki.com/",
    "https://breezewiki.nadeko.net/",
    "https://breezewiki.pussthecat.org/",
    "https://bw.projectsegfau.lt/",
    ledit: hashPath('https://ledit.lol'),
    libmedium: [
    'https://libmedium.batsense.net',
    'https://md.vern.cc',
    'https://medium.hostux.net',
    'https://libmedium.batsense.net',
    ],
    neuters: ["https://neuters.de", "https://nu.vern.cc"],
    ruraldictionary: [
    "https://rd.vern.cc",
    "https://rd.bloat.cat",
    "https://rd.thirtysix.pw",
    nerdsfornerds: ['https://nn.vern.cc'],
    neuters: ['https://neuters.de', 'https://nu.vern.cc'],
    nitter: [
    'https://xcancel.com',
    'https://nitter.privacyredirect.com',
    'https://lightbrd.com',
    'https://nitter.space',
    'https://nitter.tiekoetter.com',
    'https://nuku.trabun.org',
    'https://nitter.net',
    ],
    libmedium: [
    "https://libmedium.batsense.net",
    "https://md.vern.cc",
    "https://medium.hostux.net",
    "https://libmedium.batsense.net/",
    'photon-reddit': ['https://photon-reddit.com'],
    priviblur: [
    'https://pb.bloat.cat',
    'https://tb.opnxng.com',
    'https://priviblur.pussthecat.org',
    'https://priviblur.thebunny.zone',
    'https://priviblur.canine.tools',
    'https://pb.cleberg.net',
    'https://tumblr.nerdvpn.de',
    ],
    dumb: [
    "https://dm.vern.cc",
    "https://dumb.lunar.icu",
    "https://dumb.ducks.party",
    "https://dumb.hyperreal.coffee",
    "https://dumb.bloat.cat",
    "https://dumb.jeikobu.net",
    "https://dumb.canine.tools",
    "https://lyr.dc09.ru",
    "https://db.kuuro.net",
    "https://dumb.ducks.party/",
    "https://dumb.lunar.icu/",
    quetre: [
    'https://quetre.iket.me',
    'https://qr.vern.cc',
    'https://quetre.pussthecat.org',
    'https://ask.habedieeh.re',
    'https://quetre.blackdrgn.nl',
    'https://quetre.lunar.icu',
    'https://quetre.drgns.space',
    'https://quetre.r4fo.com',
    'https://quetre.ducks.party',
    'https://quetre.private.coffee',
    'https://quetre.canine.tools',
    'https://qt.bloat.cat',
    'https://quetre.jeikobu.net',
    'https://q.307200.xyz',
    'https://quora.nerdvpn.de',
    'https://quetre.privacyredirect.com',
    'https://quora.vern.cc',
    'https://quetre.pufe.org',
    'https://questions.whateveritworks.org',
    'https://quetre.odyssey346.dev',
    'https://quetre.pussthecat.org',
    ],
    anonymousoverflow: [
    "https://code.whatever.social",
    "https://ao.vern.cc",
    "https://overflow.lunar.icu",
    "https://overflow.adminforge.de",
    "https://overflow.hostux.net",
    "https://overflow.projectsegfau.lt",
    "https://overflow.fascinated.cc",
    "https://ao.bloat.cat",
    "https://ao.owo.si",
    "https://overflow.freedit.eu",
    "https://anonoverflow.hyperreal.coffee",
    "https://overflow.r4fo.com",
    "https://overflow.ducks.party",
    "https://overflow.snine.nl",
    "https://anonymousoverflow.privacyredirect.com",
    "https://soflow.nerdvpn.de",
    "https://overflow.einfachzocken.eu",
    "https://overflow.seasi.dev",
    "https://anonymousoverflow.catsarch.com",
    "https://overflow.darkness.services",
    "https://anonflow.aketawi.space",
    "https://overflow.canine.tools",
    "https://ao.bloat.cat/",
    reddiculous: hashPath(
    'https://ashish-um.github.io/reddiculous',
    'https://mescalito.github.io/Reddiculous',
    ),
    rdx: [
    function rdx(url) {
    const path = url.pathname;

    const userPattern = /^\/u(?:ser)?\/([^/]+)/;
    const subPattern = /^\/r\/([^/]+)/;

    if (path.includes('/comments/')) {
    url.searchParams.set('url', url.href);
    url.pathname = '/comments.html';
    } else if (userPattern.test(path)) {
    const matches = path.match(userPattern);
    url.searchParams.set('u', matches[1]);
    url.pathname = '/user.html';
    } else if (subPattern.test(path)) {
    const matches = path.match(subPattern);
    url.searchParams.set('r', matches[1]);
    url.pathname = '/subreddit.html';
    }

    url.hostname = 'rdx.overdevs.com';

    return url;
    },
    ],
    priviblur: [
    "https://pb.bloat.cat",
    "https://tb.opnxng.com",
    "https://priviblur.pussthecat.org",
    "https://priviblur.thebunny.zone",
    "https://priviblur.canine.tools",
    "https://pb.cleberg.net",
    "https://tumblr.nerdvpn.de",
    reditr: ['https://reditr.com/'],
    redlib: [
    'https://redlib.catsarch.com',
    'https://redlib.perennialte.ch',
    'https://redlib.privacyredirect.com',
    'https://reddit.nerdvpn.de',
    'https://reddit.adminforge.de',
    'https://rl.blitzw.in',
    'https://redlib.orangenet.cc',
    'https://redlib.privadency.com',
    'https://l.opnxng.com',
    'https://red.artemislena.eu',
    'https://redlib.nadeko.net',
    'https://rl.bloat.cat',
    'https://safereddit.com',
    'https://redlib.freedit.eu',
    ],
    eddrit: ["https://eddrit.com/"],
    intellectual: [
    "https://in.bloat.cat/",
    "https://intellectual.catsarch.com/",
    "https://intellectual.insprill.net/",
    rf: hashPath('https://rf.alexkoen.com'),
    ruraldictionary: [
    'https://rd.vern.cc',
    'https://rd.bloat.cat',
    'https://rd.thirtysix.pw',
    ],
    "photon-reddit": ["https://photon-reddit.com/"],
    scribe: [
    'https://scribe.rip',
    'https://scribe.nixnet.services',
    'https://scribe.citizen4.eu',
    'https://scribe.froth.zone',
    'https://scribe.rawbit.ninja',
    'https://sc.vern.cc',
    'https://scribe.r4fo.com',
    'https://scribe.privacyredirect.com',
    'https://scribe.privacyredirect.com',
    'https://scribe.rawbit.ninja',
    'https://scribe.rip',
    ],
    small: ['https://small.private.coffee'],
    };

    const replacements = Object.entries({
    @@ -196,7 +238,16 @@ function getRedirect() {
    genius: ['dumb', 'intellectual'],
    medium: ['libmedium', 'scribe', 'small'],
    quora: ['quetre'],
    reddit: ['eddrit', 'photon-reddit', 'redlib'],
    reddit: [
    'eddrit',
    'ledit',
    'photon-reddit',
    'rdx',
    'reddiculous',
    'reditr',
    'redlib',
    'rf',
    ],
    reuters: ['neuters'],
    stackoverflow: ['anonymousoverflow'],
    tumblr: ['priviblur'],
    @@ -211,6 +262,8 @@ function getRedirect() {
    );

    if (replacement) {
    const isFunction = (input) => typeof input === 'function';

    const chooseRandom = (list) =>
    list.at(Math.floor(Math.random() * list.length));

    @@ -221,14 +274,15 @@ function getRedirect() {
    if (instancesForType?.length > 0) {
    const instance = chooseRandom(instancesForType);

    const redirect = new URL(location.pathname, instance);
    const redirect = isFunction(instance)
    ? instance(new URL(location))
    : new URL(location.pathname, instance);

    return redirect;
    }
    }

    const redirect = new URL('https://farside.link');
    redirect.pathname = `/_/${location.href}`;
    const redirect = new URL(`/_/${location.href}`, 'https://farside.link');

    return redirect;
    }
  8. examosa revised this gist Aug 14, 2025. 1 changed file with 139 additions and 225 deletions.
    364 changes: 139 additions & 225 deletions shortside.user.js
    Original file line number Diff line number Diff line change
    @@ -1,6 +1,6 @@
    // ==UserScript==
    // @name Shortside
    // @version 1.3.1
    // @version 1.3.2
    // @description Redirects the configured websites to third-party privacy-respecting front ends.
    // @author examosa
    // @license AGPLv3-or-later
    @@ -27,253 +27,167 @@

    function getRedirect() {
    const instances = {
    ducksforducks: ['https://ducksforducks.private.coffee'],
    nerdsfornerds: ['https://nn.vern.cc'],
    ducksforducks: ["https://ducksforducks.private.coffee"],
    nerdsfornerds: ["https://nn.vern.cc"],
    invidious: [
    'https://inv.nadeko.net',
    'https://invidious.nerdvpn.de',
    'https://invidious.f5.si',
    'https://yewtu.be',
    'https://invidious.snopyta.org',
    ],
    piped: [
    'https://watch.whatever.social',
    'https://piped.garudalinux.org',
    'https://piped.lunar.icu',
    'https://piped.projectsegfau.lt',
    'https://piped.privacydev.net',
    'https://piped.smnz.de',
    'https://piped.adminforge.de',
    'https://piped.qdi.fi',
    'https://piped.hostux.net',
    'https://pd.vern.cc',
    'https://piped.colinslegacy.com',
    'https://piped.ducks.party',
    'https://piped.video',
    'https://efy.piped.pages.dev/',
    'https://il.ax/',
    'https://pa.il.ax/',
    'https://piped.dedyn.io/',
    'https://piped.drgns.space/',
    'https://piped.kavin.rocks/',
    'https://piped.mha.fi/',
    'https://piped.minionflo.net/',
    'https://piped.palveluntarjoaja.eu/',
    'https://piped.private.coffee/',
    'https://piped.reallyaweso.me/',
    'https://piped.tokhmi.xyz/',
    'https://piped.video/',
    "https://inv.nadeko.net",
    "https://invidious.nerdvpn.de",
    "https://invidious.f5.si",
    "https://yewtu.be",
    "https://invidious.snopyta.org",
    ],
    nitter: [
    'https://xcancel.com',
    'https://nitter.poast.org',
    'https://nitter.privacyredirect.com',
    'https://lightbrd.com',
    'https://nitter.space',
    'https://nitter.tiekoetter.com',
    'https://nuku.trabun.org',
    'https://nitter.privacydev.net',
    'https://nitter.net',
    "https://xcancel.com",
    "https://nitter.privacyredirect.com",
    "https://lightbrd.com",
    "https://nitter.space",
    "https://nitter.tiekoetter.com",
    "https://nuku.trabun.org",
    "https://nitter.net",
    ],
    redlib: [
    'https://redlib.catsarch.com',
    'https://redlib.perennialte.ch',
    'https://redlib.tux.pizza',
    'https://redlib.ducks.party',
    'https://r.darrennathanael.com',
    'https://redlib.privacyredirect.com',
    'https://reddit.nerdvpn.de',
    'https://redlib.baczek.me',
    'https://reddit.adminforge.de',
    'https://rl.blitzw.in',
    'https://redlib.orangenet.cc',
    'https://redlib.privadency.com',
    'https://redlib.minihoot.site',
    'https://l.opnxng.com',
    'https://red.arancia.click',
    'https://red.artemislena.eu',
    'https://red.ngn.tf',
    'https://redlib.kittywi.re',
    'https://redlib.nadeko.net',
    'https://redlib.privacy.com.de',
    'https://rl.bloat.cat',
    'https://rl.rootdo.com',
    'https://safereddit.com',
    'https://redlib.freedit.eu',
    "https://redlib.catsarch.com",
    "https://redlib.perennialte.ch",
    "https://redlib.privacyredirect.com",
    "https://reddit.nerdvpn.de",
    "https://reddit.adminforge.de",
    "https://rl.blitzw.in",
    "https://redlib.orangenet.cc",
    "https://redlib.privadency.com",
    "https://l.opnxng.com",
    "https://red.artemislena.eu",
    "https://redlib.nadeko.net",
    "https://rl.bloat.cat",
    "https://safereddit.com",
    "https://redlib.freedit.eu",
    ],
    scribe: [
    'https://scribe.rip',
    'https://scribe.nixnet.services',
    'https://scribe.citizen4.eu',
    'https://scribe.bus-hit.me',
    'https://scribe.froth.zone',
    'https://scribe.privacydev.net',
    'https://scribe.rawbit.ninja',
    'https://sc.vern.cc',
    'https://m.opnxng.com',
    'https://scribe.manasiwibi.com',
    'https://scribe.r4fo.com',
    'https://scribe.privacyredirect.com',
    'https://scribe.privacyredirect.com/',
    'https://scribe.rawbit.ninja/',
    'https://scribe.rip/',
    "https://scribe.rip",
    "https://scribe.nixnet.services",
    "https://scribe.citizen4.eu",
    "https://scribe.froth.zone",
    "https://scribe.rawbit.ninja",
    "https://sc.vern.cc",
    "https://scribe.r4fo.com",
    "https://scribe.privacyredirect.com",
    "https://scribe.privacyredirect.com/",
    "https://scribe.rawbit.ninja/",
    "https://scribe.rip/",
    ],
    small: ['https://small.private.coffee'],
    small: ["https://small.private.coffee"],
    quetre: [
    'https://quetre.iket.me',
    'https://qr.vern.cc',
    'https://quetre.pussthecat.org',
    'https://quetre.tokhmi.xyz',
    'https://quetre.privacydev.net',
    'https://ask.habedieeh.re',
    'https://quetre.blackdrgn.nl',
    'https://quetre.lunar.icu',
    'https://q.opnxng.com',
    'https://ask.sudovanilla.org',
    'https://quetre.drgns.space',
    'https://quetre.r4fo.com',
    'https://quetre.ducks.party',
    'https://quetre.nadeko.net',
    'https://quetre.private.coffee',
    'https://quetre.canine.tools',
    'https://qt.bloat.cat',
    'https://quetre.jeikobu.net',
    'https://quetre.franklyflawless.org',
    'https://q.307200.xyz',
    'https://quora.nerdvpn.de',
    'https://quetre.privacyredirect.com',
    'https://quora.vern.cc',
    'https://quetre.projectsegfau.lt',
    'https://quetre.esmailelbob.xyz',
    'https://quetre.marcopisco.com',
    'https://quetre.pufe.org',
    'https://que.wilbvr.me',
    'https://questions.whateveritworks.org',
    'https://quetre.catsarch.com',
    'https://quetre.frontendfriendly.xyz',
    'https://quetre.odyssey346.dev/',
    'https://quetre.pussthecat.org/',
    "https://quetre.iket.me",
    "https://qr.vern.cc",
    "https://quetre.pussthecat.org",
    "https://ask.habedieeh.re",
    "https://quetre.blackdrgn.nl",
    "https://quetre.lunar.icu",
    "https://quetre.drgns.space",
    "https://quetre.r4fo.com",
    "https://quetre.ducks.party",
    "https://quetre.private.coffee",
    "https://quetre.canine.tools",
    "https://qt.bloat.cat",
    "https://quetre.jeikobu.net",
    "https://q.307200.xyz",
    "https://quora.nerdvpn.de",
    "https://quetre.privacyredirect.com",
    "https://quora.vern.cc",
    "https://quetre.pufe.org",
    "https://questions.whateveritworks.org",
    "https://quetre.odyssey346.dev/",
    "https://quetre.pussthecat.org/",
    ],
    breezewiki: [
    'https://breezewiki.com',
    'https://antifandom.com',
    'https://breezewiki.pussthecat.org',
    'https://bw.hamstro.dev',
    'https://bw.projectsegfau.lt',
    'https://breeze.hostux.net',
    'https://bw.artemislena.eu',
    'https://nerd.whatever.social',
    'https://breezewiki.frontendfriendly.xyz',
    'https://breeze.nohost.network',
    'https://breeze.whateveritworks.org',
    'https://z.opnxng.com',
    'https://breezewiki.hyperreal.coffee',
    'https://breezewiki.catsarch.com',
    'https://breeze.mint.lgbt',
    'https://breezewiki.woodland.cafe',
    'https://breezewiki.nadeko.net',
    'https://fandom.reallyaweso.me',
    'https://breezewiki.4o1x5.dev',
    'https://breezewiki.r4fo.com',
    'https://breezewiki.private.coffee',
    'https://fan.blitzw.in',
    'https://antifandom.com/',
    'https://breezewiki.com/',
    'https://breezewiki.nadeko.net/',
    'https://breezewiki.pussthecat.org/',
    'https://bw.hamstro.dev/',
    'https://bw.projectsegfau.lt/',
    'https://z.opnxng.com/',
    "https://breezewiki.com",
    "https://antifandom.com",
    "https://breezewiki.pussthecat.org",
    "https://bw.projectsegfau.lt",
    "https://breeze.hostux.net",
    "https://bw.artemislena.eu",
    "https://breeze.nohost.network",
    "https://breeze.whateveritworks.org",
    "https://z.opnxng.com",
    "https://breezewiki.hyperreal.coffee",
    "https://breezewiki.catsarch.com",
    "https://breeze.mint.lgbt",
    "https://breezewiki.nadeko.net",
    "https://fandom.reallyaweso.me",
    "https://breezewiki.r4fo.com",
    "https://breezewiki.private.coffee",
    "https://fan.blitzw.in",
    "https://antifandom.com/",
    "https://breezewiki.com/",
    "https://breezewiki.nadeko.net/",
    "https://breezewiki.pussthecat.org/",
    "https://bw.projectsegfau.lt/",
    ],
    neuters: ['https://neuters.de', 'https://nu.vern.cc'],
    neuters: ["https://neuters.de", "https://nu.vern.cc"],
    ruraldictionary: [
    'https://rd.vern.cc',
    'https://rd.bloat.cat',
    'https://rd.thirtysix.pw',
    'https://rd.cmc.pub',
    'https://ruraldictionary.franklyflawless.org',
    "https://rd.vern.cc",
    "https://rd.bloat.cat",
    "https://rd.thirtysix.pw",
    ],
    libmedium: [
    'https://libmedium.batsense.net',
    'https://md.vern.cc',
    'https://medium.hostux.net',
    'https://r.sudovanilla.org',
    'https://libmedium.ducks.party',
    'https://libmedium.batsense.net/',
    'https://libmedium.ducks.party/',
    "https://libmedium.batsense.net",
    "https://md.vern.cc",
    "https://medium.hostux.net",
    "https://libmedium.batsense.net/",
    ],
    dumb: [
    'https://dm.vern.cc',
    'https://sing.whatever.social',
    'https://dumb.lunar.icu',
    'https://dumb.privacydev.net',
    'https://dumb.ducks.party',
    'https://dumb.privacyfucking.rocks',
    'https://dumb.hyperreal.coffee',
    'https://dumb.bloat.cat',
    'https://dumb.jeikobu.net',
    'https://dumb.canine.tools',
    'https://lyr.dc09.ru',
    'https://db.kuuro.net',
    'https://dumb.nunosempere.com',
    'https://dumb.esmailelbob.xyz',
    'https://dumb.ducks.party/',
    'https://dumb.lunar.icu/',
    "https://dm.vern.cc",
    "https://dumb.lunar.icu",
    "https://dumb.ducks.party",
    "https://dumb.hyperreal.coffee",
    "https://dumb.bloat.cat",
    "https://dumb.jeikobu.net",
    "https://dumb.canine.tools",
    "https://lyr.dc09.ru",
    "https://db.kuuro.net",
    "https://dumb.ducks.party/",
    "https://dumb.lunar.icu/",
    ],
    anonymousoverflow: [
    'https://code.whatever.social',
    'https://ao.vern.cc',
    'https://overflow.smnz.de',
    'https://overflow.lunar.icu',
    'https://overflow.adminforge.de',
    'https://overflow.hostux.net',
    'https://overflow.projectsegfau.lt',
    'https://code.xbdm.fun',
    'https://overflow.fascinated.cc',
    'https://ao.bloat.cat',
    'https://anonoverflow.frontendfriendly.xyz',
    'https://ao.owo.si',
    'https://overflow.datura.network',
    'https://overflow.freedit.eu',
    'https://ao.rootdo.com',
    'https://anonoverflow.hyperreal.coffee',
    'https://o.sudovanilla.org',
    'https://anonymousoverflow.privacyfucking.rocks',
    'https://exchange.seitan-ayoub.lol',
    'https://overflow.r4fo.com',
    'https://overflow.ducks.party',
    'https://ao.ngn.tf',
    'https://overflow.snine.nl',
    'https://anonymousoverflow.privacyredirect.com',
    'https://soflow.nerdvpn.de',
    'https://overflow.einfachzocken.eu',
    'https://overflow.seasi.dev',
    'https://anonymousoverflow.catsarch.com',
    'https://overflow.darkness.services',
    'https://anonflow.aketawi.space',
    'https://ao.bunk.lol',
    'https://o.iii.st',
    'https://overflow.canine.tools',
    'https://ao.bloatcat.tk',
    'https://ao.bloat.cat/',
    'https://ao.ngn.tf/',
    "https://code.whatever.social",
    "https://ao.vern.cc",
    "https://overflow.lunar.icu",
    "https://overflow.adminforge.de",
    "https://overflow.hostux.net",
    "https://overflow.projectsegfau.lt",
    "https://overflow.fascinated.cc",
    "https://ao.bloat.cat",
    "https://ao.owo.si",
    "https://overflow.freedit.eu",
    "https://anonoverflow.hyperreal.coffee",
    "https://overflow.r4fo.com",
    "https://overflow.ducks.party",
    "https://overflow.snine.nl",
    "https://anonymousoverflow.privacyredirect.com",
    "https://soflow.nerdvpn.de",
    "https://overflow.einfachzocken.eu",
    "https://overflow.seasi.dev",
    "https://anonymousoverflow.catsarch.com",
    "https://overflow.darkness.services",
    "https://anonflow.aketawi.space",
    "https://overflow.canine.tools",
    "https://ao.bloat.cat/",
    ],
    priviblur: [
    'https://pb.bloat.cat',
    'https://tb.opnxng.com',
    'https://priviblur.pussthecat.org',
    'https://priviblur.thebunny.zone',
    'https://priviblur.canine.tools',
    'https://pb.cleberg.net',
    'https://tumblr.nerdvpn.de',
    "https://pb.bloat.cat",
    "https://tb.opnxng.com",
    "https://priviblur.pussthecat.org",
    "https://priviblur.thebunny.zone",
    "https://priviblur.canine.tools",
    "https://pb.cleberg.net",
    "https://tumblr.nerdvpn.de",
    ],
    eddrit: ['https://eddrit.com/'],
    eddrit: ["https://eddrit.com/"],
    intellectual: [
    'https://in.bloat.cat/',
    'https://intellectual.catsarch.com/',
    'https://intellectual.insprill.net/',
    "https://in.bloat.cat/",
    "https://intellectual.catsarch.com/",
    "https://intellectual.insprill.net/",
    ],
    'photon-reddit': ['https://photon-reddit.com/'],
    "photon-reddit": ["https://photon-reddit.com/"],
    };

    const replacements = Object.entries({
    @@ -289,7 +203,7 @@ function getRedirect() {
    twitter: ['nitter'],
    urbandictionary: ['ruraldictionary'],
    'x.com': ['nitter'],
    youtube: ['invidious', 'piped'],
    youtube: ['invidious'],
    });

    const replacement = replacements.find(([host]) =>
  9. examosa revised this gist Aug 6, 2025. 1 changed file with 2 additions and 2 deletions.
    4 changes: 2 additions & 2 deletions shortside.user.js
    Original file line number Diff line number Diff line change
    @@ -1,6 +1,6 @@
    // ==UserScript==
    // @name Shortside
    // @version 1.3.0
    // @version 1.3.1
    // @description Redirects the configured websites to third-party privacy-respecting front ends.
    // @author examosa
    // @license AGPLv3-or-later
    @@ -10,6 +10,7 @@
    // @match https://www.geeksforgeeks.org/*
    // @match https://genius.com/*
    // @match https://www.medium.com/*
    // @match https://medium.com/*
    // @match https://stackoverflow.com/*
    // @match https://www.reddit.com/*
    // @match https://*.youtube.com/*
    @@ -62,7 +63,6 @@ function getRedirect() {
    'https://piped.reallyaweso.me/',
    'https://piped.tokhmi.xyz/',
    'https://piped.video/',
    'https://pipedapi.palveluntarjoaja.eu/',
    ],
    nitter: [
    'https://xcancel.com',
  10. examosa revised this gist Jul 29, 2025. 2 changed files with 381 additions and 203 deletions.
    100 changes: 100 additions & 0 deletions fetch-instances.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,100 @@
    const cdn = (path) => new URL(path, 'https://nobsdelivr.private.coffee/');

    const withCatch =
    (action) =>
    (...args) =>
    Promise.try(action, ...args).catch((error) => {
    console.error('Caught error:', error);
    });

    const getJson = withCatch((url) =>
    fetch(url, { headers: { Accept: 'application/json' } }).then((response) =>
    response.json(),
    ),
    );

    async function testBackups() {
    const backups = [
    'https://jsonblob.com/api/jsonBlob/1392945386074857472',
    'https://api.npoint.io/6ee2cb1b5a1aa6f10be5',
    'https://api.pastes.dev/VSjbFP63yq',
    'https://api.codetabs.com/v1/proxy/?quest=https%3A%2F%2Fstarb.in%2Fraw%2FMgDjym',
    'https://bytebin.lucko.me/bDAxqrUNqD',
    ];

    const results = await Promise.all(backups.map((backup) => getJson(backup)));

    for (const index of results.keys()) {
    const result = results[index];

    if (!result) {
    const backup = backups[index];
    console.warn(`Stale backup: ${backup}`);
    }
    }
    }

    async function fetchInstances() {
    const instances = {
    ducksforducks: ['https://ducksforducks.private.coffee'],
    nerdsfornerds: ['https://nn.vern.cc'],
    };

    const providers = {
    libredirect: {
    source: 'libredirect/instances/data.json',
    normalize: (result) =>
    Object.entries(result).map(([name, instances]) => ({
    type: name.toLowerCase(),
    instances: instances.clearnet,
    })),
    },

    farside: {
    source: 'benbusby/farside/services.json',
    normalize: (result) =>
    result.map((service) => ({
    type: service.type,
    instances: service.instances.concat(service.fallback),
    })),
    },

    fastside: {
    source: 'cofob/fastside/services.json',
    normalize: (result) =>
    result.services.map((service) => ({
    type: service.type,
    instances: service.instances
    .filter((instance) => instance.tags.includes('clearnet'))
    .map((instance) => instance.url),
    })),
    },
    };

    const promises = Object.values(providers).map(async (provider) => {
    const result = await getJson(cdn(`gh/${provider.source}`));

    if (!result) {
    return [];
    }

    return provider.normalize(result);
    });

    const sources = await Promise.all(promises);

    for (const source of sources.flat()) {
    const list = (instances[source.type] ??= []);

    for (const url of source.instances) {
    if (!list.includes(url)) {
    list.push(url);
    }
    }
    }

    return instances;
    }

    await testBackups();
    await fetchInstances();
    484 changes: 281 additions & 203 deletions shortside.user.js
    Original file line number Diff line number Diff line change
    @@ -1,224 +1,295 @@
    // ==UserScript==
    // @name Shortside
    // @version 1.2.2
    // @version 1.3.0
    // @description Redirects the configured websites to third-party privacy-respecting front ends.
    // @author examosa
    // @license AGPLv3-or-later
    // @updateUrl https://gist.github.com/examosa/50eb28bc16a006b62b3f43893ab457e7/raw/shortside.user.js
    // @downloadUrl https://gist.github.com/examosa/50eb28bc16a006b62b3f43893ab457e7/raw/shortside.user.js
    // @match https://*.fandom.com/*
    // @match https://*.medium.com/*
    // @match https://www.fandom.com/*
    // @match https://www.geeksforgeeks.org/*
    // @match https://genius.com/*
    // @match https://www.medium.com/*
    // @match https://stackoverflow.com/*
    // @match https://www.reddit.com/*
    // @match https://www.youtube.com/*
    // @match https://*.youtube.com/*
    // @match https://www.quora.com/*
    // @match https://www.geeksforgeeks.org/*
    // @match https://www.reuters.com/*
    // @match https://www.tumblr.com/*
    // @match https://twitter.com/*
    // @match https://www.urbandictionary.com/*
    // @match https://x.com/*
    // @run-at document_start
    // @allFrames false
    // @noframes
    // ==/UserScript==

    const cdn = (path) => new URL(path, 'https://nobsdelivr.private.coffee/');

    const withCatch =
    (action) =>
    (...args) =>
    Promise.try(action, ...args).catch((error) => {
    console.error('Caught error:', error);
    });

    const getJson = withCatch((url) =>
    fetch(url, { headers: { Accept: 'application/json' } }).then((response) =>
    response.json(),
    ),
    );

    function createCache() {
    const cacheKey = 'SHORTSIDE_INSTANCES';

    const drivers = {
    vanillaPudding: {
    async isSupported() {
    const getMessage = withCatch(
    () => import(cdn('npm/@vanilla-pudding/[email protected]/+esm')),
    );

    const message = await getMessage();
    const ext = message?.useExt();

    if (!ext?.getVersion()) {
    this.isSupported = () => false;
    return false;
    }

    const store = ext.bgt.extLocalStore;

    Object.assign(this, {
    isSupported: () => true,
    get: () => store.getByStrict(cacheKey),
    set: (value) => store.set(cacheKey, value, 10),
    });

    return true;
    },
    },

    greaseMonkey: {
    isSupported: () =>
    typeof GM === 'object' &&
    typeof GM.getValue === 'function' &&
    typeof GM.setValue === 'function',
    get: () => GM.getValue(cacheKey).then((value) => JSON.parse(value)),
    set: (value) => GM.setValue(cacheKey, JSON.stringify(value)),
    },

    localStorage: {
    isSupported: () =>
    typeof localStorage === 'object' &&
    typeof localStorage.getItem === 'function' &&
    typeof localStorage.setItem === 'function',
    get() {
    const item = localStorage.getItem(cacheKey);
    return JSON.parse(item);
    },
    async set(value) {
    localStorage.setItem(cacheKey, JSON.stringify(value));
    },
    },

    backup: {
    async isSupported() {
    // Report set() as unsupported
    this.isSupported = () => false;

    try {
    await fetch('https://httpbin.private.coffee/status/204');

    const backups = [
    'https://jsonblob.com/api/jsonBlob/1392945386074857472',
    'https://api.npoint.io/6ee2cb1b5a1aa6f10be5',
    'https://api.pastes.dev/VSjbFP63yq',
    'https://api.codetabs.com/v1/proxy/?quest=https%3A%2F%2Fstarb.in%2Fraw%2FMgDjym',
    'https://bytebin.lucko.me/bDAxqrUNqD',
    ];

    this.get = async () => {
    for (const backup of backups) {
    const result = await getJson(backup);
    if (result) return result;
    }
    };

    return true;
    } catch {
    return false;
    }
    },
    },
    };

    async function eachDriver(call) {
    const safeCall = withCatch(call);

    for (const driver of Object.values(drivers)) {
    if (!(await driver.isSupported())) continue;
    const result = await safeCall(driver);
    if (result) return result;
    }
    }

    const cache = {
    get: () => eachDriver((driver) => driver.get()),
    set: (value) => eachDriver((driver) => driver.set(value).then(() => true)),
    };

    return cache;
    }

    async function fetchInstances() {
    const cache = createCache();

    const cached = await cache.get();

    if (cached) {
    return cached;
    }

    const instances = Object.assign(Object.create(null), {
    function getRedirect() {
    const instances = {
    ducksforducks: ['https://ducksforducks.private.coffee'],
    nerdsfornerds: ['https://nn.vern.cc'],
    });

    const providers = {
    libredirect: {
    source: 'libredirect/instances/data.json',
    normalize: (result) =>
    Object.entries(result).map(([name, instances]) => ({
    type: name.toLowerCase(),
    instances: instances.clearnet,
    })),
    },

    farside: {
    source: 'benbusby/farside/services.json',
    normalize: (result) =>
    result.map((service) => ({
    type: service.type,
    instances: service.instances.concat(service.fallback),
    })),
    },

    fastside: {
    source: 'cofob/fastside/services.json',
    normalize: (result) =>
    result.services.map((service) => ({
    type: service.type,
    instances: service.instances
    .filter((instance) => instance.tags.includes('clearnet'))
    .map((instance) => instance.url),
    })),
    },
    invidious: [
    'https://inv.nadeko.net',
    'https://invidious.nerdvpn.de',
    'https://invidious.f5.si',
    'https://yewtu.be',
    'https://invidious.snopyta.org',
    ],
    piped: [
    'https://watch.whatever.social',
    'https://piped.garudalinux.org',
    'https://piped.lunar.icu',
    'https://piped.projectsegfau.lt',
    'https://piped.privacydev.net',
    'https://piped.smnz.de',
    'https://piped.adminforge.de',
    'https://piped.qdi.fi',
    'https://piped.hostux.net',
    'https://pd.vern.cc',
    'https://piped.colinslegacy.com',
    'https://piped.ducks.party',
    'https://piped.video',
    'https://efy.piped.pages.dev/',
    'https://il.ax/',
    'https://pa.il.ax/',
    'https://piped.dedyn.io/',
    'https://piped.drgns.space/',
    'https://piped.kavin.rocks/',
    'https://piped.mha.fi/',
    'https://piped.minionflo.net/',
    'https://piped.palveluntarjoaja.eu/',
    'https://piped.private.coffee/',
    'https://piped.reallyaweso.me/',
    'https://piped.tokhmi.xyz/',
    'https://piped.video/',
    'https://pipedapi.palveluntarjoaja.eu/',
    ],
    nitter: [
    'https://xcancel.com',
    'https://nitter.poast.org',
    'https://nitter.privacyredirect.com',
    'https://lightbrd.com',
    'https://nitter.space',
    'https://nitter.tiekoetter.com',
    'https://nuku.trabun.org',
    'https://nitter.privacydev.net',
    'https://nitter.net',
    ],
    redlib: [
    'https://redlib.catsarch.com',
    'https://redlib.perennialte.ch',
    'https://redlib.tux.pizza',
    'https://redlib.ducks.party',
    'https://r.darrennathanael.com',
    'https://redlib.privacyredirect.com',
    'https://reddit.nerdvpn.de',
    'https://redlib.baczek.me',
    'https://reddit.adminforge.de',
    'https://rl.blitzw.in',
    'https://redlib.orangenet.cc',
    'https://redlib.privadency.com',
    'https://redlib.minihoot.site',
    'https://l.opnxng.com',
    'https://red.arancia.click',
    'https://red.artemislena.eu',
    'https://red.ngn.tf',
    'https://redlib.kittywi.re',
    'https://redlib.nadeko.net',
    'https://redlib.privacy.com.de',
    'https://rl.bloat.cat',
    'https://rl.rootdo.com',
    'https://safereddit.com',
    'https://redlib.freedit.eu',
    ],
    scribe: [
    'https://scribe.rip',
    'https://scribe.nixnet.services',
    'https://scribe.citizen4.eu',
    'https://scribe.bus-hit.me',
    'https://scribe.froth.zone',
    'https://scribe.privacydev.net',
    'https://scribe.rawbit.ninja',
    'https://sc.vern.cc',
    'https://m.opnxng.com',
    'https://scribe.manasiwibi.com',
    'https://scribe.r4fo.com',
    'https://scribe.privacyredirect.com',
    'https://scribe.privacyredirect.com/',
    'https://scribe.rawbit.ninja/',
    'https://scribe.rip/',
    ],
    small: ['https://small.private.coffee'],
    quetre: [
    'https://quetre.iket.me',
    'https://qr.vern.cc',
    'https://quetre.pussthecat.org',
    'https://quetre.tokhmi.xyz',
    'https://quetre.privacydev.net',
    'https://ask.habedieeh.re',
    'https://quetre.blackdrgn.nl',
    'https://quetre.lunar.icu',
    'https://q.opnxng.com',
    'https://ask.sudovanilla.org',
    'https://quetre.drgns.space',
    'https://quetre.r4fo.com',
    'https://quetre.ducks.party',
    'https://quetre.nadeko.net',
    'https://quetre.private.coffee',
    'https://quetre.canine.tools',
    'https://qt.bloat.cat',
    'https://quetre.jeikobu.net',
    'https://quetre.franklyflawless.org',
    'https://q.307200.xyz',
    'https://quora.nerdvpn.de',
    'https://quetre.privacyredirect.com',
    'https://quora.vern.cc',
    'https://quetre.projectsegfau.lt',
    'https://quetre.esmailelbob.xyz',
    'https://quetre.marcopisco.com',
    'https://quetre.pufe.org',
    'https://que.wilbvr.me',
    'https://questions.whateveritworks.org',
    'https://quetre.catsarch.com',
    'https://quetre.frontendfriendly.xyz',
    'https://quetre.odyssey346.dev/',
    'https://quetre.pussthecat.org/',
    ],
    breezewiki: [
    'https://breezewiki.com',
    'https://antifandom.com',
    'https://breezewiki.pussthecat.org',
    'https://bw.hamstro.dev',
    'https://bw.projectsegfau.lt',
    'https://breeze.hostux.net',
    'https://bw.artemislena.eu',
    'https://nerd.whatever.social',
    'https://breezewiki.frontendfriendly.xyz',
    'https://breeze.nohost.network',
    'https://breeze.whateveritworks.org',
    'https://z.opnxng.com',
    'https://breezewiki.hyperreal.coffee',
    'https://breezewiki.catsarch.com',
    'https://breeze.mint.lgbt',
    'https://breezewiki.woodland.cafe',
    'https://breezewiki.nadeko.net',
    'https://fandom.reallyaweso.me',
    'https://breezewiki.4o1x5.dev',
    'https://breezewiki.r4fo.com',
    'https://breezewiki.private.coffee',
    'https://fan.blitzw.in',
    'https://antifandom.com/',
    'https://breezewiki.com/',
    'https://breezewiki.nadeko.net/',
    'https://breezewiki.pussthecat.org/',
    'https://bw.hamstro.dev/',
    'https://bw.projectsegfau.lt/',
    'https://z.opnxng.com/',
    ],
    neuters: ['https://neuters.de', 'https://nu.vern.cc'],
    ruraldictionary: [
    'https://rd.vern.cc',
    'https://rd.bloat.cat',
    'https://rd.thirtysix.pw',
    'https://rd.cmc.pub',
    'https://ruraldictionary.franklyflawless.org',
    ],
    libmedium: [
    'https://libmedium.batsense.net',
    'https://md.vern.cc',
    'https://medium.hostux.net',
    'https://r.sudovanilla.org',
    'https://libmedium.ducks.party',
    'https://libmedium.batsense.net/',
    'https://libmedium.ducks.party/',
    ],
    dumb: [
    'https://dm.vern.cc',
    'https://sing.whatever.social',
    'https://dumb.lunar.icu',
    'https://dumb.privacydev.net',
    'https://dumb.ducks.party',
    'https://dumb.privacyfucking.rocks',
    'https://dumb.hyperreal.coffee',
    'https://dumb.bloat.cat',
    'https://dumb.jeikobu.net',
    'https://dumb.canine.tools',
    'https://lyr.dc09.ru',
    'https://db.kuuro.net',
    'https://dumb.nunosempere.com',
    'https://dumb.esmailelbob.xyz',
    'https://dumb.ducks.party/',
    'https://dumb.lunar.icu/',
    ],
    anonymousoverflow: [
    'https://code.whatever.social',
    'https://ao.vern.cc',
    'https://overflow.smnz.de',
    'https://overflow.lunar.icu',
    'https://overflow.adminforge.de',
    'https://overflow.hostux.net',
    'https://overflow.projectsegfau.lt',
    'https://code.xbdm.fun',
    'https://overflow.fascinated.cc',
    'https://ao.bloat.cat',
    'https://anonoverflow.frontendfriendly.xyz',
    'https://ao.owo.si',
    'https://overflow.datura.network',
    'https://overflow.freedit.eu',
    'https://ao.rootdo.com',
    'https://anonoverflow.hyperreal.coffee',
    'https://o.sudovanilla.org',
    'https://anonymousoverflow.privacyfucking.rocks',
    'https://exchange.seitan-ayoub.lol',
    'https://overflow.r4fo.com',
    'https://overflow.ducks.party',
    'https://ao.ngn.tf',
    'https://overflow.snine.nl',
    'https://anonymousoverflow.privacyredirect.com',
    'https://soflow.nerdvpn.de',
    'https://overflow.einfachzocken.eu',
    'https://overflow.seasi.dev',
    'https://anonymousoverflow.catsarch.com',
    'https://overflow.darkness.services',
    'https://anonflow.aketawi.space',
    'https://ao.bunk.lol',
    'https://o.iii.st',
    'https://overflow.canine.tools',
    'https://ao.bloatcat.tk',
    'https://ao.bloat.cat/',
    'https://ao.ngn.tf/',
    ],
    priviblur: [
    'https://pb.bloat.cat',
    'https://tb.opnxng.com',
    'https://priviblur.pussthecat.org',
    'https://priviblur.thebunny.zone',
    'https://priviblur.canine.tools',
    'https://pb.cleberg.net',
    'https://tumblr.nerdvpn.de',
    ],
    eddrit: ['https://eddrit.com/'],
    intellectual: [
    'https://in.bloat.cat/',
    'https://intellectual.catsarch.com/',
    'https://intellectual.insprill.net/',
    ],
    'photon-reddit': ['https://photon-reddit.com/'],
    };

    try {
    const promises = Object.values(providers).map(async (provider) => {
    const result = await getJson(cdn(`gh/${provider.source}`));

    if (!result) {
    return [];
    }

    return provider.normalize(result);
    });

    const sources = await Promise.all(promises).flat();

    for (const source of sources) {
    const list = (instances[source.type] ??= []);

    for (const url of source.instances) {
    if (!list.includes(url)) {
    list.push(url);
    }
    }
    }

    await cache.set(instances);
    } catch {}

    return instances;
    }

    async function run() {
    const instances = await fetchInstances();

    const replacements = Object.entries({
    fandom: ['breezewiki'],
    geeksforgeeks: ['ducksforducks', 'nerdsfornerds'],
    medium: ['libmedium', 'scribe'],
    genius: ['dumb', 'intellectual'],
    medium: ['libmedium', 'scribe', 'small'],
    quora: ['quetre'],
    reddit: ['eddrit', 'photon-reddit', 'redlib'],
    reuters: ['neuters'],
    stackoverflow: ['anonymousoverflow'],
    tumblr: ['priviblur'],
    twitter: ['nitter'],
    urbandictionary: ['ruraldictionary'],
    'x.com': ['nitter'],
    youtube: ['invidious', 'piped'],
    });

    const replacement = replacements.find(([host]) =>
    @@ -234,16 +305,23 @@ async function run() {
    const instancesForType = instances[type];

    if (instancesForType?.length > 0) {
    const redirect = new URL(
    location.pathname,
    chooseRandom(instancesForType),
    );
    const instance = chooseRandom(instancesForType);

    const redirect = new URL(location.pathname, instance);

    return location.replace(redirect);
    return redirect;
    }
    }

    location.replace(`https://farside.link/${location.href}`);
    const redirect = new URL('https://farside.link');
    redirect.pathname = `/_/${location.href}`;

    return redirect;
    }

    function main() {
    const redirect = getRedirect();
    location.replace(redirect);
    }

    run();
    main();
  11. examosa revised this gist Jul 15, 2025. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion shortside.user.js
    Original file line number Diff line number Diff line change
    @@ -1,6 +1,6 @@
    // ==UserScript==
    // @name Shortside
    // @version 1.2.1
    // @version 1.2.2
    // @description Redirects the configured websites to third-party privacy-respecting front ends.
    // @author examosa
    // @license AGPLv3-or-later
  12. examosa revised this gist Jul 15, 2025. 1 changed file with 23 additions and 31 deletions.
    54 changes: 23 additions & 31 deletions shortside.user.js
    Original file line number Diff line number Diff line change
    @@ -47,6 +47,7 @@ function createCache() {
    const ext = message?.useExt();

    if (!ext?.getVersion()) {
    this.isSupported = () => false;
    return false;
    }

    @@ -72,7 +73,10 @@ function createCache() {
    },

    localStorage: {
    isSupported: () => true,
    isSupported: () =>
    typeof localStorage === 'object' &&
    typeof localStorage.getItem === 'function' &&
    typeof localStorage.setItem === 'function',
    get() {
    const item = localStorage.getItem(cacheKey);
    return JSON.parse(item);
    @@ -84,6 +88,9 @@ function createCache() {

    backup: {
    async isSupported() {
    // Report set() as unsupported
    this.isSupported = () => false;

    try {
    await fetch('https://httpbin.private.coffee/status/204');

    @@ -95,33 +102,18 @@ function createCache() {
    'https://bytebin.lucko.me/bDAxqrUNqD',
    ];

    Object.assign(this, {
    isSupported: () => true,
    async get() {
    const result = await Promise.any(
    backups.map((backup) => getJson(backup)),
    );

    this.get = () => result;
    return result;
    },
    });
    this.get = async () => {
    for (const backup of backups) {
    const result = await getJson(backup);
    if (result) return result;
    }
    };

    return true;
    } catch {
    return false;
    }
    },
    set(value) {
    const original = this.isSupported;

    this.isSupported = function () {
    this.isSupported = original;
    return false;
    };

    return cache.set(value);
    },
    },
    };

    @@ -217,18 +209,18 @@ async function fetchInstances() {
    return instances;
    }

    const replacements = Object.entries({
    fandom: ['breezewiki'],
    geeksforgeeks: ['ducksforducks', 'nerdsfornerds'],
    medium: ['libmedium', 'scribe'],
    quora: ['quetre'],
    reddit: ['eddrit', 'photon-reddit', 'redlib'],
    stackoverflow: ['anonymousoverflow'],
    });

    async function run() {
    const instances = await fetchInstances();

    const replacements = Object.entries({
    fandom: ['breezewiki'],
    geeksforgeeks: ['ducksforducks', 'nerdsfornerds'],
    medium: ['libmedium', 'scribe'],
    quora: ['quetre'],
    reddit: ['eddrit', 'photon-reddit', 'redlib'],
    stackoverflow: ['anonymousoverflow'],
    });

    const replacement = replacements.find(([host]) =>
    location.hostname.includes(host),
    );
  13. examosa revised this gist Jul 14, 2025. 1 changed file with 10 additions and 3 deletions.
    13 changes: 10 additions & 3 deletions shortside.user.js
    Original file line number Diff line number Diff line change
    @@ -1,6 +1,6 @@
    // ==UserScript==
    // @name Shortside
    // @version 1.2.0
    // @version 1.2.1
    // @description Redirects the configured websites to third-party privacy-respecting front ends.
    // @author examosa
    // @license AGPLv3-or-later
    @@ -91,13 +91,20 @@ function createCache() {
    'https://jsonblob.com/api/jsonBlob/1392945386074857472',
    'https://api.npoint.io/6ee2cb1b5a1aa6f10be5',
    'https://api.pastes.dev/VSjbFP63yq',
    `https://api.cors.lol?url=${encodeURIComponent('https://starb.in/raw/MgDjym')}`,
    'https://api.codetabs.com/v1/proxy/?quest=https%3A%2F%2Fstarb.in%2Fraw%2FMgDjym',
    'https://bytebin.lucko.me/bDAxqrUNqD',
    ];

    Object.assign(this, {
    isSupported: () => true,
    get: () => Promise.any(backups.map((backup) => getJson(backup))),
    async get() {
    const result = await Promise.any(
    backups.map((backup) => getJson(backup)),
    );

    this.get = () => result;
    return result;
    },
    });

    return true;
  14. examosa revised this gist Jul 14, 2025. 2 changed files with 69 additions and 69 deletions.
    28 changes: 0 additions & 28 deletions redirect-to-farside.user.js
    Original file line number Diff line number Diff line change
    @@ -1,28 +0,0 @@
    // ==UserScript==
    // @name Redirect to Farside
    // @version 1.5
    // @description Redirects the configured websites to third-party privacy-respecting front ends.
    // @author examosa
    // @license AGPLv3-or-later
    // @updateUrl https://gist.github.com/examosa/50eb28bc16a006b62b3f43893ab457e7/raw/redirect-to-farside.user.js
    // @match https://*.fandom.com/*
    // @match https://*.medium.com/*
    // @match https://stackoverflow.com/*
    // @match https://www.reddit.com/*
    // @match https://www.youtube.com/*
    // @match https://www.quora.com/*
    // @match https://www.geeksforgeeks.org/*
    // @runAt document_start
    // ==/UserScript==

    const replacements = Object.entries({
    'www.geeksforgeeks.org': 'ducksforducks.private.coffee',
    });

    const replacement = replacements.find(([hostname]) => hostname === location.hostname);

    const target = replacement
    ? location.href.replace(...replacement)
    : `https://farside.link/${location.href}`;

    location.replace(target);
    110 changes: 69 additions & 41 deletions shortside.user.js
    Original file line number Diff line number Diff line change
    @@ -1,6 +1,6 @@
    // ==UserScript==
    // @name Shortside
    // @version 1.1.2
    // @version 1.2.0
    // @description Redirects the configured websites to third-party privacy-respecting front ends.
    // @author examosa
    // @license AGPLv3-or-later
    @@ -36,65 +36,93 @@ const getJson = withCatch((url) =>
    function createCache() {
    const cacheKey = 'SHORTSIDE_INSTANCES';

    const backups = [
    'https://jsonblob.com/api/jsonBlob/1392945386074857472',
    'https://api.npoint.io/6ee2cb1b5a1aa6f10be5',
    'https://api.pastes.dev/VSjbFP63yq',
    'https://starb.in/raw/MgDjym',
    'https://bytebin.lucko.me/bDAxqrUNqD',
    ];
    const drivers = {
    vanillaPudding: {
    async isSupported() {
    const getMessage = withCatch(
    () => import(cdn('npm/@vanilla-pudding/[email protected]/+esm')),
    );

    const driverOf = (get, set) => ({ get, set });
    const message = await getMessage();
    const ext = message?.useExt();

    async function withStore(call) {
    const message = await import(
    cdn('npm/@vanilla-pudding/[email protected]/+esm')
    );
    if (!ext?.getVersion()) {
    return false;
    }

    const ext = message.useExt();
    return call(ext.bgt.extLocalStore);
    }
    const store = ext.bgt.extLocalStore;

    const drivers = {
    vanillaPudding: driverOf(
    () => withStore((store) => store.getByStrict(cacheKey)),
    (value) => withStore((store) => store.set(cacheKey, value, 10)),
    ),

    greaseMonkey: driverOf(
    () => GM.getValue(cacheKey).then((value) => JSON.parse(value)),
    (value) => GM.setValue(cacheKey, JSON.stringify(value)),
    ),

    localStorage: driverOf(
    () => {
    Object.assign(this, {
    isSupported: () => true,
    get: () => store.getByStrict(cacheKey),
    set: (value) => store.set(cacheKey, value, 10),
    });

    return true;
    },
    },

    greaseMonkey: {
    isSupported: () =>
    typeof GM === 'object' &&
    typeof GM.getValue === 'function' &&
    typeof GM.setValue === 'function',
    get: () => GM.getValue(cacheKey).then((value) => JSON.parse(value)),
    set: (value) => GM.setValue(cacheKey, JSON.stringify(value)),
    },

    localStorage: {
    isSupported: () => true,
    get() {
    const item = localStorage.getItem(cacheKey);
    return JSON.parse(item);
    },
    (value) => {
    async set(value) {
    localStorage.setItem(cacheKey, JSON.stringify(value));
    },
    ),
    },

    backup: driverOf(
    async () => {
    for (const backup of backups) {
    const result = await getJson(backup);
    if (result) return result;
    backup: {
    async isSupported() {
    try {
    await fetch('https://httpbin.private.coffee/status/204');

    const backups = [
    'https://jsonblob.com/api/jsonBlob/1392945386074857472',
    'https://api.npoint.io/6ee2cb1b5a1aa6f10be5',
    'https://api.pastes.dev/VSjbFP63yq',
    `https://api.cors.lol?url=${encodeURIComponent('https://starb.in/raw/MgDjym')}`,
    'https://bytebin.lucko.me/bDAxqrUNqD',
    ];

    Object.assign(this, {
    isSupported: () => true,
    get: () => Promise.any(backups.map((backup) => getJson(backup))),
    });

    return true;
    } catch {
    return false;
    }
    },
    (value) => {
    if (backups.skip) return backups.skip = false;
    backups.skip = true;
    set(value) {
    const original = this.isSupported;

    this.isSupported = function () {
    this.isSupported = original;
    return false;
    };

    return cache.set(value);
    },
    ),
    },
    };

    async function eachDriver(call) {
    const safeCall = withCatch(call);

    for (const driver of Object.values(drivers)) {
    if (!(await driver.isSupported())) continue;
    const result = await safeCall(driver);
    if (result) return result;
    }
    @@ -219,4 +247,4 @@ async function run() {
    location.replace(`https://farside.link/${location.href}`);
    }

    run();
    run();
  15. examosa revised this gist Jul 11, 2025. 1 changed file with 3 additions and 3 deletions.
    6 changes: 3 additions & 3 deletions shortside.user.js
    Original file line number Diff line number Diff line change
    @@ -1,6 +1,6 @@
    // ==UserScript==
    // @name Shortside
    // @version 1.1.1
    // @version 1.1.2
    // @description Redirects the configured websites to third-party privacy-respecting front ends.
    // @author examosa
    // @license AGPLv3-or-later
    @@ -92,10 +92,10 @@ function createCache() {
    };

    async function eachDriver(call) {
    const callSafe = withCatch(call);
    const safeCall = withCatch(call);

    for (const driver of Object.values(drivers)) {
    const result = await callSafe(driver, ...args);
    const result = await safeCall(driver);
    if (result) return result;
    }
    }
  16. examosa revised this gist Jul 11, 2025. 1 changed file with 4 additions and 2 deletions.
    6 changes: 4 additions & 2 deletions shortside.user.js
    Original file line number Diff line number Diff line change
    @@ -1,6 +1,6 @@
    // ==UserScript==
    // @name Shortside
    // @version 1.1.0
    // @version 1.1.1
    // @description Redirects the configured websites to third-party privacy-respecting front ends.
    // @author examosa
    // @license AGPLv3-or-later
    @@ -13,7 +13,9 @@
    // @match https://www.youtube.com/*
    // @match https://www.quora.com/*
    // @match https://www.geeksforgeeks.org/*
    // @runAt document_start
    // @run-at document_start
    // @allFrames false
    // @noframes
    // ==/UserScript==

    const cdn = (path) => new URL(path, 'https://nobsdelivr.private.coffee/');
  17. examosa revised this gist Jul 11, 2025. 1 changed file with 110 additions and 90 deletions.
    200 changes: 110 additions & 90 deletions shortside.user.js
    Original file line number Diff line number Diff line change
    @@ -1,6 +1,6 @@
    // ==UserScript==
    // @name Shortside
    // @version 1.0.0
    // @version 1.1.0
    // @description Redirects the configured websites to third-party privacy-respecting front ends.
    // @author examosa
    // @license AGPLv3-or-later
    @@ -18,109 +18,96 @@

    const cdn = (path) => new URL(path, 'https://nobsdelivr.private.coffee/');

    const chooseRandom = (list) => list.at(Math.floor(Math.random() * list.length));
    const withCatch =
    (action) =>
    (...args) =>
    Promise.try(action, ...args).catch((error) => {
    console.error('Caught error:', error);
    });

    async function createCache() {
    const cacheKey = '1392945386074857472';
    const getJson = withCatch((url) =>
    fetch(url, { headers: { Accept: 'application/json' } }).then((response) =>
    response.json(),
    ),
    );

    // Greasemonkey API
    if (typeof GM === 'object') {
    const gmCache = {
    async get() {
    try {
    const value = await GM.getValue(cacheKey);
    return JSON.parse(value);
    } catch {
    return null;
    }
    },
    function createCache() {
    const cacheKey = 'SHORTSIDE_INSTANCES';

    set: (value) => GM.setValue(cacheKey, JSON.stringify(value)),
    };
    const backups = [
    'https://jsonblob.com/api/jsonBlob/1392945386074857472',
    'https://api.npoint.io/6ee2cb1b5a1aa6f10be5',
    'https://api.pastes.dev/VSjbFP63yq',
    'https://starb.in/raw/MgDjym',
    'https://bytebin.lucko.me/bDAxqrUNqD',
    ];

    return gmCache;
    }
    const driverOf = (get, set) => ({ get, set });

    // Vanilla Pudding
    async function withStore(call) {
    const message = await import(
    cdn('npm/@vanilla-pudding/[email protected]/+esm')
    );

    const store = await import(
    cdn('npm/@vanilla-pudding/[email protected]/+esm')
    ).then(
    (module) => {
    const ext = module.useExt();
    const ext = message.useExt();
    return call(ext.bgt.extLocalStore);
    }

    if (!ext.getVersion()) {
    return null;
    }
    const drivers = {
    vanillaPudding: driverOf(
    () => withStore((store) => store.getByStrict(cacheKey)),
    (value) => withStore((store) => store.set(cacheKey, value, 10)),
    ),

    greaseMonkey: driverOf(
    () => GM.getValue(cacheKey).then((value) => JSON.parse(value)),
    (value) => GM.setValue(cacheKey, JSON.stringify(value)),
    ),

    localStorage: driverOf(
    () => {
    const item = localStorage.getItem(cacheKey);
    return JSON.parse(item);
    },
    (value) => {
    localStorage.setItem(cacheKey, JSON.stringify(value));
    },
    ),

    return ext.bgt.extLocalStore;
    },
    () => null,
    );
    backup: driverOf(
    async () => {
    for (const backup of backups) {
    const result = await getJson(backup);
    if (result) return result;
    }
    },
    (value) => {
    if (backups.skip) return backups.skip = false;
    backups.skip = true;
    return cache.set(value);
    },
    ),
    };

    if (store) {
    const messageCache = {
    get: () => store.getByStrict(cacheKey),
    set: (value) => store.set(cacheKey, value, 10),
    };
    async function eachDriver(call) {
    const callSafe = withCatch(call);

    return messageCache;
    for (const driver of Object.values(drivers)) {
    const result = await callSafe(driver, ...args);
    if (result) return result;
    }
    }

    // JSON Blob
    const blobCache = {
    async get() {
    const response = await fetch(
    `https://jsonblob.com/api/jsonBlob/${cacheKey}`,
    );

    if (response.ok) {
    return await response.json();
    }

    return null;
    },
    set() {
    console.warn('Unable to cache with JSON Blob');
    },
    const cache = {
    get: () => eachDriver((driver) => driver.get()),
    set: (value) => eachDriver((driver) => driver.set(value).then(() => true)),
    };

    return blobCache;
    return cache;
    }

    const providers = {
    libredirect: {
    source: 'libredirect/instances/data.json',
    normalize: (result) =>
    Object.entries(result).map(([name, instances]) => ({
    type: name.toLowerCase(),
    instances: instances.clearnet,
    })),
    },

    farside: {
    source: 'benbusby/farside/services.json',
    normalize: (result) =>
    result.map((service) => ({
    type: service.type,
    instances: service.instances.concat(service.fallback),
    })),
    },

    fastside: {
    source: 'cofob/fastside/services.json',
    normalize: (result) =>
    result.services.map((service) => ({
    type: service.type,
    instances: service.instances
    .filter((instance) => instance.tags.includes('clearnet'))
    .map((instance) => instance.url),
    })),
    },
    };

    async function fetchInstances() {
    const cache = await createCache();
    const cache = createCache();

    const cached = await cache.get();

    @@ -133,15 +120,45 @@ async function fetchInstances() {
    nerdsfornerds: ['https://nn.vern.cc'],
    });

    const providers = {
    libredirect: {
    source: 'libredirect/instances/data.json',
    normalize: (result) =>
    Object.entries(result).map(([name, instances]) => ({
    type: name.toLowerCase(),
    instances: instances.clearnet,
    })),
    },

    farside: {
    source: 'benbusby/farside/services.json',
    normalize: (result) =>
    result.map((service) => ({
    type: service.type,
    instances: service.instances.concat(service.fallback),
    })),
    },

    fastside: {
    source: 'cofob/fastside/services.json',
    normalize: (result) =>
    result.services.map((service) => ({
    type: service.type,
    instances: service.instances
    .filter((instance) => instance.tags.includes('clearnet'))
    .map((instance) => instance.url),
    })),
    },
    };

    try {
    const promises = Object.values(providers).map(async (provider) => {
    const response = await fetch(cdn(`gh/${provider.source}`));
    const result = await getJson(cdn(`gh/${provider.source}`));

    if (!response.ok) {
    if (!result) {
    return [];
    }

    const result = await response.json();
    return provider.normalize(result);
    });

    @@ -180,6 +197,9 @@ async function run() {
    );

    if (replacement) {
    const chooseRandom = (list) =>
    list.at(Math.floor(Math.random() * list.length));

    const [, options] = replacement;
    const type = chooseRandom(options);
    const instancesForType = instances[type];
  18. examosa revised this gist Jul 10, 2025. 1 changed file with 200 additions and 0 deletions.
    200 changes: 200 additions & 0 deletions shortside.user.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,200 @@
    // ==UserScript==
    // @name Shortside
    // @version 1.0.0
    // @description Redirects the configured websites to third-party privacy-respecting front ends.
    // @author examosa
    // @license AGPLv3-or-later
    // @updateUrl https://gist.github.com/examosa/50eb28bc16a006b62b3f43893ab457e7/raw/shortside.user.js
    // @downloadUrl https://gist.github.com/examosa/50eb28bc16a006b62b3f43893ab457e7/raw/shortside.user.js
    // @match https://*.fandom.com/*
    // @match https://*.medium.com/*
    // @match https://stackoverflow.com/*
    // @match https://www.reddit.com/*
    // @match https://www.youtube.com/*
    // @match https://www.quora.com/*
    // @match https://www.geeksforgeeks.org/*
    // @runAt document_start
    // ==/UserScript==

    const cdn = (path) => new URL(path, 'https://nobsdelivr.private.coffee/');

    const chooseRandom = (list) => list.at(Math.floor(Math.random() * list.length));

    async function createCache() {
    const cacheKey = '1392945386074857472';

    // Greasemonkey API
    if (typeof GM === 'object') {
    const gmCache = {
    async get() {
    try {
    const value = await GM.getValue(cacheKey);
    return JSON.parse(value);
    } catch {
    return null;
    }
    },

    set: (value) => GM.setValue(cacheKey, JSON.stringify(value)),
    };

    return gmCache;
    }

    // Vanilla Pudding

    const store = await import(
    cdn('npm/@vanilla-pudding/[email protected]/+esm')
    ).then(
    (module) => {
    const ext = module.useExt();

    if (!ext.getVersion()) {
    return null;
    }

    return ext.bgt.extLocalStore;
    },
    () => null,
    );

    if (store) {
    const messageCache = {
    get: () => store.getByStrict(cacheKey),
    set: (value) => store.set(cacheKey, value, 10),
    };

    return messageCache;
    }

    // JSON Blob
    const blobCache = {
    async get() {
    const response = await fetch(
    `https://jsonblob.com/api/jsonBlob/${cacheKey}`,
    );

    if (response.ok) {
    return await response.json();
    }

    return null;
    },
    set() {
    console.warn('Unable to cache with JSON Blob');
    },
    };

    return blobCache;
    }

    const providers = {
    libredirect: {
    source: 'libredirect/instances/data.json',
    normalize: (result) =>
    Object.entries(result).map(([name, instances]) => ({
    type: name.toLowerCase(),
    instances: instances.clearnet,
    })),
    },

    farside: {
    source: 'benbusby/farside/services.json',
    normalize: (result) =>
    result.map((service) => ({
    type: service.type,
    instances: service.instances.concat(service.fallback),
    })),
    },

    fastside: {
    source: 'cofob/fastside/services.json',
    normalize: (result) =>
    result.services.map((service) => ({
    type: service.type,
    instances: service.instances
    .filter((instance) => instance.tags.includes('clearnet'))
    .map((instance) => instance.url),
    })),
    },
    };

    async function fetchInstances() {
    const cache = await createCache();

    const cached = await cache.get();

    if (cached) {
    return cached;
    }

    const instances = Object.assign(Object.create(null), {
    ducksforducks: ['https://ducksforducks.private.coffee'],
    nerdsfornerds: ['https://nn.vern.cc'],
    });

    try {
    const promises = Object.values(providers).map(async (provider) => {
    const response = await fetch(cdn(`gh/${provider.source}`));

    if (!response.ok) {
    return [];
    }

    const result = await response.json();
    return provider.normalize(result);
    });

    const sources = await Promise.all(promises).flat();

    for (const source of sources) {
    const list = (instances[source.type] ??= []);

    for (const url of source.instances) {
    if (!list.includes(url)) {
    list.push(url);
    }
    }
    }

    await cache.set(instances);
    } catch {}

    return instances;
    }

    const replacements = Object.entries({
    fandom: ['breezewiki'],
    geeksforgeeks: ['ducksforducks', 'nerdsfornerds'],
    medium: ['libmedium', 'scribe'],
    quora: ['quetre'],
    reddit: ['eddrit', 'photon-reddit', 'redlib'],
    stackoverflow: ['anonymousoverflow'],
    });

    async function run() {
    const instances = await fetchInstances();

    const replacement = replacements.find(([host]) =>
    location.hostname.includes(host),
    );

    if (replacement) {
    const [, options] = replacement;
    const type = chooseRandom(options);
    const instancesForType = instances[type];

    if (instancesForType?.length > 0) {
    const redirect = new URL(
    location.pathname,
    chooseRandom(instancesForType),
    );

    return location.replace(redirect);
    }
    }

    location.replace(`https://farside.link/${location.href}`);
    }

    run();
  19. examosa revised this gist Jul 10, 2025. No changes.
  20. examosa revised this gist Jun 23, 2025. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion redirect-to-farside.user.js
    Original file line number Diff line number Diff line change
    @@ -1,6 +1,6 @@
    // ==UserScript==
    // @name Redirect to Farside
    // @version 1.4
    // @version 1.5
    // @description Redirects the configured websites to third-party privacy-respecting front ends.
    // @author examosa
    // @license AGPLv3-or-later
  21. examosa revised this gist Jun 23, 2025. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion redirect-to-farside.user.js
    Original file line number Diff line number Diff line change
    @@ -5,7 +5,7 @@
    // @author examosa
    // @license AGPLv3-or-later
    // @updateUrl https://gist.github.com/examosa/50eb28bc16a006b62b3f43893ab457e7/raw/redirect-to-farside.user.js
    // @match https://www.fandom.com/*
    // @match https://*.fandom.com/*
    // @match https://*.medium.com/*
    // @match https://stackoverflow.com/*
    // @match https://www.reddit.com/*
  22. examosa renamed this gist Jun 17, 2025. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion redirect-to-farside.js → redirect-to-farside.user.js
    Original file line number Diff line number Diff line change
    @@ -4,7 +4,7 @@
    // @description Redirects the configured websites to third-party privacy-respecting front ends.
    // @author examosa
    // @license AGPLv3-or-later
    // @updateUrl https://gist.github.com/examosa/50eb28bc16a006b62b3f43893ab457e7/raw/redirect-to-farside.js
    // @updateUrl https://gist.github.com/examosa/50eb28bc16a006b62b3f43893ab457e7/raw/redirect-to-farside.user.js
    // @match https://www.fandom.com/*
    // @match https://*.medium.com/*
    // @match https://stackoverflow.com/*
  23. examosa revised this gist Jun 7, 2025. 1 changed file with 2 additions and 4 deletions.
    6 changes: 2 additions & 4 deletions redirect-to-farside.js
    Original file line number Diff line number Diff line change
    @@ -4,17 +4,15 @@
    // @description Redirects the configured websites to third-party privacy-respecting front ends.
    // @author examosa
    // @license AGPLv3-or-later
    // @homepageURL https://gist.github.com/examosa/50eb28bc16a006b62b3f43893ab457e7
    // @downloadURL https://gist.github.com/examosa/50eb28bc16a006b62b3f43893ab457e7/raw/redirect-to-farside.js
    // @updateURL https://gist.github.com/examosa/50eb28bc16a006b62b3f43893ab457e7/raw/redirect-to-farside.js
    // @updateUrl https://gist.github.com/examosa/50eb28bc16a006b62b3f43893ab457e7/raw/redirect-to-farside.js
    // @match https://www.fandom.com/*
    // @match https://*.medium.com/*
    // @match https://stackoverflow.com/*
    // @match https://www.reddit.com/*
    // @match https://www.youtube.com/*
    // @match https://www.quora.com/*
    // @match https://www.geeksforgeeks.org/*
    // @run-at document_start
    // @runAt document_start
    // ==/UserScript==

    const replacements = Object.entries({
  24. examosa revised this gist Jun 6, 2025. 1 changed file with 4 additions and 2 deletions.
    6 changes: 4 additions & 2 deletions redirect-to-farside.js
    Original file line number Diff line number Diff line change
    @@ -1,10 +1,12 @@
    // ==UserScript==
    // @name Redirect to Farside
    // @version 1.3
    // @version 1.4
    // @description Redirects the configured websites to third-party privacy-respecting front ends.
    // @author examosa
    // @license AGPLv3-or-later
    // @update-url https://gist.github.com/examosa/50eb28bc16a006b62b3f43893ab457e7/raw/redirect-to-farside.js
    // @homepageURL https://gist.github.com/examosa/50eb28bc16a006b62b3f43893ab457e7
    // @downloadURL https://gist.github.com/examosa/50eb28bc16a006b62b3f43893ab457e7/raw/redirect-to-farside.js
    // @updateURL https://gist.github.com/examosa/50eb28bc16a006b62b3f43893ab457e7/raw/redirect-to-farside.js
    // @match https://www.fandom.com/*
    // @match https://*.medium.com/*
    // @match https://stackoverflow.com/*
  25. examosa revised this gist Jun 3, 2025. 1 changed file with 2 additions and 2 deletions.
    4 changes: 2 additions & 2 deletions redirect-to-farside.js
    Original file line number Diff line number Diff line change
    @@ -1,6 +1,6 @@
    // ==UserScript==
    // @name Redirect to Farside
    // @version 1.2
    // @version 1.3
    // @description Redirects the configured websites to third-party privacy-respecting front ends.
    // @author examosa
    // @license AGPLv3-or-later
    @@ -25,4 +25,4 @@ const target = replacement
    ? location.href.replace(...replacement)
    : `https://farside.link/${location.href}`;

    location.assign(target);
    location.replace(target);
  26. examosa revised this gist May 12, 2025. 1 changed file with 3 additions and 3 deletions.
    6 changes: 3 additions & 3 deletions redirect-to-farside.js
    Original file line number Diff line number Diff line change
    @@ -1,18 +1,18 @@
    // ==UserScript==
    // @name Redirect to Farside
    // @version 1.1
    // @version 1.2
    // @description Redirects the configured websites to third-party privacy-respecting front ends.
    // @author examosa
    // @license AGPLv3-or-later
    // @update-url https://gist.github.com/examosa/50eb28bc16a006b62b3f43893ab457e7/raw/redirect-to-farside.js
    // @match https://www.fandom.com/*
    // @match https://medium.com/*
    // @match https://*.medium.com/*
    // @match https://stackoverflow.com/*
    // @match https://www.reddit.com/*
    // @match https://www.youtube.com/*
    // @match https://www.quora.com/*
    // @match https://www.geeksforgeeks.org/*
    // @run-at document-start
    // @run-at document_start
    // ==/UserScript==

    const replacements = Object.entries({
  27. examosa revised this gist Feb 5, 2025. 1 changed file with 2 additions and 1 deletion.
    3 changes: 2 additions & 1 deletion redirect-to-farside.js
    Original file line number Diff line number Diff line change
    @@ -1,11 +1,12 @@
    // ==UserScript==
    // @name Redirect to Farside
    // @version 1.0
    // @version 1.1
    // @description Redirects the configured websites to third-party privacy-respecting front ends.
    // @author examosa
    // @license AGPLv3-or-later
    // @update-url https://gist.github.com/examosa/50eb28bc16a006b62b3f43893ab457e7/raw/redirect-to-farside.js
    // @match https://www.fandom.com/*
    // @match https://medium.com/*
    // @match https://stackoverflow.com/*
    // @match https://www.reddit.com/*
    // @match https://www.youtube.com/*
  28. examosa revised this gist Jan 10, 2025. 1 changed file with 3 additions and 2 deletions.
    5 changes: 3 additions & 2 deletions redirect-to-farside.js
    Original file line number Diff line number Diff line change
    @@ -5,6 +5,7 @@
    // @author examosa
    // @license AGPLv3-or-later
    // @update-url https://gist.github.com/examosa/50eb28bc16a006b62b3f43893ab457e7/raw/redirect-to-farside.js
    // @match https://www.fandom.com/*
    // @match https://stackoverflow.com/*
    // @match https://www.reddit.com/*
    // @match https://www.youtube.com/*
    @@ -14,13 +15,13 @@
    // ==/UserScript==

    const replacements = Object.entries({
    'www.geeksforgeeks.org': 'nn.vern.cc',
    'www.geeksforgeeks.org': 'ducksforducks.private.coffee',
    });

    const replacement = replacements.find(([hostname]) => hostname === location.hostname);

    const target = replacement
    ? location.href.replace(...replacement)
    : `https://cf.farside.link/${location.href}`;
    : `https://farside.link/${location.href}`;

    location.assign(target);
  29. examosa revised this gist Oct 11, 2024. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion redirect-to-farside.js
    Original file line number Diff line number Diff line change
    @@ -14,7 +14,7 @@
    // ==/UserScript==

    const replacements = Object.entries({
    'www.geeksforgeeks.org': 'nerds.hyperreal.coffee',
    'www.geeksforgeeks.org': 'nn.vern.cc',
    });

    const replacement = replacements.find(([hostname]) => hostname === location.hostname);
  30. examosa revised this gist Oct 8, 2024. 1 changed file with 1 addition and 0 deletions.
    1 change: 1 addition & 0 deletions redirect-to-farside.js
    Original file line number Diff line number Diff line change
    @@ -4,6 +4,7 @@
    // @description Redirects the configured websites to third-party privacy-respecting front ends.
    // @author examosa
    // @license AGPLv3-or-later
    // @update-url https://gist.github.com/examosa/50eb28bc16a006b62b3f43893ab457e7/raw/redirect-to-farside.js
    // @match https://stackoverflow.com/*
    // @match https://www.reddit.com/*
    // @match https://www.youtube.com/*