Skip to content

Instantly share code, notes, and snippets.

@examosa
Last active January 8, 2026 15:42
Show Gist options
  • Select an option

  • Save examosa/50eb28bc16a006b62b3f43893ab457e7 to your computer and use it in GitHub Desktop.

Select an option

Save examosa/50eb28bc16a006b62b3f43893ab457e7 to your computer and use it in GitHub Desktop.
Userscripts
// ==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);
// ==UserScript==
// @name Shortside
// @version 1.1.2
// @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/*
// @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 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 driverOf = (get, set) => ({ get, set });
async function withStore(call) {
const message = await import(
cdn('npm/@vanilla-pudding/message@1.4.0/+esm')
);
const ext = message.useExt();
return call(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(
() => {
const item = localStorage.getItem(cacheKey);
return JSON.parse(item);
},
(value) => {
localStorage.setItem(cacheKey, JSON.stringify(value));
},
),
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);
},
),
};
async function eachDriver(call) {
const safeCall = withCatch(call);
for (const driver of Object.values(drivers)) {
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), {
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),
})),
},
};
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;
}
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 chooseRandom = (list) =>
list.at(Math.floor(Math.random() * list.length));
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();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment