sharkey: init at 2025.4.3 (#411635)

This commit is contained in:
Wolfgang Walther
2025-07-06 16:15:02 +00:00
committed by GitHub
7 changed files with 534 additions and 1 deletions
+8
View File
@@ -23891,6 +23891,14 @@
githubId = 18124752;
email = "m@rvinvogt.com";
};
srxl = {
name = "Ruby Iris Juric";
email = "ruby@srxl.me";
matrix = "@ruby:isincredibly.gay";
github = "Sorixelle";
githubId = 38685302;
keys = [ { fingerprint = "2D76 76C7 A28E 16FC 75C7 268D 1B55 6ED8 4B0E 303A"; } ];
};
Srylax = {
name = "Srylax";
email = "srylax+nixpkgs@srylax.dev";
@@ -52,7 +52,9 @@
- [SuiteNumérique Docs](https://github.com/suitenumerique/docs), a collaborative note taking, wiki and documentation web platform and alternative to Notion or Outline. Available as [services.lasuite-docs](#opt-services.lasuite-docs.enable).
[dwl](https://codeberg.org/dwl/dwl), a compact, hackable compositor for Wayland based on wlroots. Available as [programs.dwl](#opt-programs.dwl.enable).
- [dwl](https://codeberg.org/dwl/dwl), a compact, hackable compositor for Wayland based on wlroots. Available as [programs.dwl](#opt-programs.dwl.enable).
- [Sharkey](https://joinsharkey.org), a Sharkish microblogging platform. Available as [services.sharkey](#opt-services.sharkey.enable).
- [mautrix-discord](https://github.com/mautrix/discord), a Matrix-Discord puppeting/relay bridge. Available as [services.mautrix-discord](#opt-services.mautrix-discord.enable).
+1
View File
@@ -1660,6 +1660,7 @@
./services/web-apps/screego.nix
./services/web-apps/selfoss.nix
./services/web-apps/sftpgo.nix
./services/web-apps/sharkey.nix
./services/web-apps/shiori.nix
./services/web-apps/silverbullet.nix
./services/web-apps/simplesamlphp.nix
+300
View File
@@ -0,0 +1,300 @@
{
config,
lib,
pkgs,
...
}:
let
cfg = config.services.sharkey;
settingsFormat = pkgs.formats.yaml { };
configFile = settingsFormat.generate "config.yml" cfg.settings;
in
{
options.services.sharkey =
let
inherit (lib)
mkEnableOption
mkOption
mkPackageOption
types
;
in
{
enable = mkEnableOption "Sharkey, a Sharkish microblogging platform";
package = mkPackageOption pkgs "sharkey" { };
environmentFiles = mkOption {
type = types.listOf types.path;
default = [ ];
example = [ "/run/secrets/sharkey-env" ];
description = ''
List of paths to files containing environment variables for Sharkey to use at runtime.
This is useful for keeping secrets out of the Nix store. See
https://docs.joinsharkey.org/docs/install/configuration/ for how to configure Sharkey using environment
variables.
'';
};
openFirewall = mkOption {
type = types.bool;
default = false;
example = true;
description = ''
Whether to open ports in the NixOS firewall for Sharkey.
'';
};
setupMeilisearch = mkOption {
type = types.bool;
default = false;
example = true;
description = ''
Whether to automatically set up a local Meilisearch instance and configure Sharkey to use it.
You need to ensure `services.meilisearch.masterKeyEnvironmentFile` is correctly configured for a working
Meilisearch setup. You also need to configure Sharkey to use an API key obtained from Meilisearch with the
`MK_CONFIG_MEILISEARCH_APIKEY` environment variable, and set `services.sharkey.settings.meilisearch.index` to
the created index. See https://docs.joinsharkey.org/docs/customisation/search/meilisearch/ for how to create
an API key and index.
'';
};
setupPostgresql = mkOption {
type = types.bool;
default = true;
example = false;
description = ''
Whether to automatically set up a local PostgreSQL database and configure Sharkey to use it.
'';
};
setupRedis = mkOption {
type = types.bool;
default = true;
example = false;
description = ''
Whether to automatically set up a local Redis cache and configure Sharkey to use it.
'';
};
settings = mkOption {
type = types.submodule {
freeformType = settingsFormat.type;
options = {
url = mkOption {
type = types.str;
example = "https://blahaj.social/";
description = ''
The full URL that the Sharkey instance will be publically accessible on.
Do NOT change this after initial setup!
'';
};
port = mkOption {
type = types.port;
default = 3000;
description = ''
The port that Sharkey will listen on.
'';
};
address = mkOption {
type = types.str;
default = "0.0.0.0";
example = "127.0.0.1";
description = ''
The address that Sharkey binds to.
'';
};
socket = mkOption {
type = types.nullOr types.path;
default = null;
example = "/run/sharkey/sharkey.sock";
description = ''
If specified, creates a UNIX socket at the given path that Sharkey listens on.
'';
};
mediaDirectory = mkOption {
type = types.path;
default = "/var/lib/sharkey";
description = ''
Path to the folder where Sharkey stores uploaded media such as images and attachments.
'';
};
fulltextSearch.provider = mkOption {
type = types.enum [
"sqlLike"
"sqlPgroonga"
"sqlTsvector"
"meilisearch"
];
default = "sqlLike";
example = "sqlPgroonga";
description = ''
Which provider to use for full text search.
All options other than `sqlLike` require extra setup - see the comments in
https://activitypub.software/TransFem-org/Sharkey/-/blob/develop/.config/example.yml for details.
If `sqlPgroonga` is set, and `services.sharkey.setupPostgres` is `true`, the pgroonga extension will
automatically be setup. You still need to create an index manually.
If using Meilisearch, consider setting `services.sharkey.setupMeilisearch` instead, which will
configure Meilisearch for you.
'';
};
id = mkOption {
type = types.enum [
"aid"
"aidx"
"meid"
"ulid"
"objectid"
];
default = "aidx";
description = ''
The ID generation method for Sharkey to use.
Do NOT change this after initial setup!
'';
};
};
};
default = { };
description = ''
Configuration options for Sharkey.
See https://activitypub.software/TransFem-org/Sharkey/-/blob/develop/.config/example.yml for a list of all
available configuration options.
'';
};
};
config =
let
inherit (lib) mkDefault mkIf mkMerge;
in
mkIf cfg.enable (mkMerge [
{
systemd.services.sharkey = {
description = "Sharkey";
documentation = [ "https://docs.joinsharkey.org/" ];
wantedBy = [ "multi-user.target" ];
startLimitBurst = 5;
startLimitIntervalSec = 60;
environment.MISSKEY_CONFIG_DIR = "/etc/sharkey";
serviceConfig = {
Type = "simple";
ExecStart = "${lib.getExe cfg.package} migrateandstart";
EnvironmentFile = cfg.environmentFiles;
DynamicUser = true;
TimeoutSec = 60;
Restart = "always";
SyslogIdentifier = "sharkey";
ConfigurationDirectory = "sharkey";
RuntimeDirectory = "sharkey";
StateDirectory = "sharkey";
CapabilityBoundingSet = "";
LockPersonality = true;
NoNewPrivileges = true;
PrivateDevices = true;
PrivateUsers = true;
PrivateTmp = true;
ProcSubset = "pid";
ProtectClock = true;
ProtectControlGroups = true;
ProtectHome = true;
ProtectHostname = true;
ProtectKernelLogs = true;
ProtectKernelModules = true;
ProtectKernelTunables = true;
ProtectProc = "invisible";
ProtectSystem = "strict";
ReadWritePaths = [ cfg.settings.mediaDirectory ];
RestrictAddressFamilies = "AF_INET AF_INET6 AF_UNIX AF_NETLINK";
RestrictNamespaces = true;
RestrictRealtime = true;
SystemCallArchitectures = "native";
SystemCallFilter = [
"~@cpu-emulation @debug @mount @obsolete @privileged @resources"
"@chown"
];
UMask = "0077";
};
};
environment.etc."sharkey/default.yml".source = configFile;
}
(mkIf cfg.openFirewall {
networking.firewall.allowedTCPPorts = [ cfg.settings.port ];
})
(mkIf cfg.setupMeilisearch {
services.meilisearch = {
enable = mkDefault true;
environment = mkDefault "production";
};
services.sharkey.settings = {
fulltextSearch.provider = "meilisearch";
meilisearch = {
host = config.services.meilisearch.listenAddress;
port = config.services.meilisearch.listenPort;
};
};
systemd.services.sharkey = {
after = [ "meilisearch.service" ];
wants = [ "meilisearch.service" ];
};
})
(mkIf cfg.setupPostgresql {
services.postgresql = {
enable = mkDefault true;
ensureDatabases = [ "sharkey" ];
ensureUsers = [
{
name = "sharkey";
ensureDBOwnership = true;
}
];
extensions = mkIf (cfg.settings.fulltextSearch.provider == "sqlPgroonga") (ps: [ ps.pgroonga ]);
};
services.sharkey.settings.db = {
host = "/run/postgresql";
db = "sharkey";
};
systemd.services.sharkey = {
after = [ "postgresql.target" ];
bindsTo = [ "postgresql.target" ];
};
})
(mkIf cfg.setupRedis {
services.redis.servers.sharkey.enable = mkDefault true;
services.sharkey.settings.redis.path = config.services.redis.servers.sharkey.unixSocket;
systemd.services.sharkey = {
after = [ "redis-sharkey.service" ];
bindsTo = [ "redis-sharkey.service" ];
serviceConfig.SupplementaryGroups = [
config.services.redis.servers.sharkey.group
];
};
})
]);
meta.maintainers = with lib.maintainers; [ srxl ];
}
+1
View File
@@ -1277,6 +1277,7 @@ in
sgt-puzzles = runTest ./sgt-puzzles.nix;
shadow = runTest ./shadow.nix;
shadowsocks = handleTest ./shadowsocks { };
sharkey = runTest ./web-apps/sharkey.nix;
shattered-pixel-dungeon = runTest ./shattered-pixel-dungeon.nix;
shiori = runTest ./shiori.nix;
signal-desktop = runTest ./signal-desktop.nix;
+55
View File
@@ -0,0 +1,55 @@
{ lib, ... }:
let
meilisearchKey = "TESTKEY-naXRkVX7nhvLaGOmGGuicDKxZAj0khEaoOZPeEZafv8w9j8V6aKb0NVdXRChL5kR";
in
{
name = "sharkey";
nodes.machine =
{ pkgs, ... }:
{
services.sharkey = {
enable = true;
setupMeilisearch = true;
environmentFiles = [ "/run/secrets/sharkey-env" ];
settings = {
url = "http://exampleurl.invalid";
meilisearch.index = "exampleurl_invalid";
};
};
services.meilisearch.masterKeyEnvironmentFile = pkgs.writeText "meilisearch-key" ''
MEILI_MASTER_KEY=${meilisearchKey}
'';
};
testScript =
let
createIndexPayload = builtins.toJSON {
description = "Sharkey API key";
actions = [ "*" ];
indexes = [ "exampleurl_invalid---notes" ];
expiresAt = null;
};
in
''
import json
with subtest("Setting up Meilisearch API key and index"):
machine.wait_for_unit("meilisearch.service")
machine.wait_for_open_port(7700)
json_body = '${createIndexPayload}'
create_index_result = json.loads(machine.succeed(f"curl -s -X POST 'http://localhost:7700/keys' -H 'Content-Type: application/json' -H 'Authorization: Bearer ${meilisearchKey}' --data-binary '{json_body}'"))
machine.succeed(f"mkdir /run/secrets; echo 'MK_CONFIG_MEILISEARCH_APIKEY={create_index_result["key"]}' > /run/secrets/sharkey-env")
with subtest("Testing Sharkey is running and listening to HTTP requests"):
machine.systemctl("restart sharkey")
machine.wait_for_open_port(3000)
machine.succeed("curl --fail http://localhost:3000")
'';
meta.maintainers = with lib.maintainers; [ srxl ];
}
+166
View File
@@ -0,0 +1,166 @@
{
stdenv,
lib,
fetchFromGitLab,
nixosTests,
bash,
cairo,
cctools,
ffmpeg-headless,
jemalloc,
makeWrapper,
nodejs,
pango,
pixman,
pkg-config,
pnpm_9,
python3,
vips,
xcbuild,
}:
stdenv.mkDerivation (finalAttrs: {
pname = "sharkey";
version = "2025.4.3";
src = fetchFromGitLab {
domain = "activitypub.software";
owner = "TransFem-org";
repo = "Sharkey";
tag = finalAttrs.version;
hash = "sha256-B268bSR5VFyJ/TaWg3xxpnP4oRj07XUpikJZ2Tb9FEY=";
fetchSubmodules = true;
};
pnpmDeps = pnpm_9.fetchDeps {
inherit (finalAttrs) pname version src;
hash = "sha256-S8LxawbtguFOEZyYbS1FQWw/TcRm4Z6mG7dUhfXbf1c=";
};
nativeBuildInputs =
[
makeWrapper
nodejs
pkg-config
pnpm_9.configHook
python3
]
++ (lib.optionals stdenv.hostPlatform.isDarwin [
cctools
xcbuild
]);
buildInputs = [
cairo
pango
pixman
vips
];
buildPhase = ''
runHook preBuild
# pnpm.fetchDeps doesn't run build scripts, so we need to run postinstall for this package otherwise the frontend
# build fails
pushd node_modules/.pnpm/node_modules/v-code-diff
pnpm postinstall
popd
# rebuild some node modules that have native dependencies
export npm_config_nodedir=${nodejs}
pushd node_modules/.pnpm/node_modules/re2
pnpm rebuild
popd
pushd node_modules/.pnpm/node_modules/sharp
pnpm run install
popd
pushd node_modules/.pnpm/node_modules/canvas
pnpm run install
popd
pnpm build
node scripts/trim-deps.mjs
pnpm prune --prod --ignore-scripts
runHook postBuild
'';
installPhase =
let
binPath = lib.makeBinPath [
bash
nodejs
pnpm_9
];
libPath = lib.makeLibraryPath [
ffmpeg-headless
jemalloc
stdenv.cc.cc
];
in
''
runHook preInstall
# adapted from repo dockerfile
# https://activitypub.software/TransFem-org/Sharkey/-/blob/develop/Dockerfile?ref_type=heads
mkdir -p $out/sharkey
cp -r built fluent-emojis node_modules package.json pnpm-workspace.yaml $out/sharkey/
mkdir -p $out/sharkey/packages/backend
cp -r packages/backend/{assets,built,migration,node_modules,ormconfig.js,package.json} $out/sharkey/packages/backend/
mkdir -p $out/sharkey/packages/backend/scripts
cp -r packages/backend/scripts/check_connect.js $out/sharkey/packages/backend/scripts/
mkdir -p $out/sharkey/packages/megalodon
cp -r packages/megalodon/{lib,node_modules,package.json} $out/sharkey/packages/megalodon/
mkdir -p $out/sharkey/packages/misskey-{bubble-game,js,reversi}
cp -r packages/misskey-bubble-game/{built,node_modules,package.json} $out/sharkey/packages/misskey-bubble-game/
cp -r packages/misskey-js/{built,node_modules,package.json} $out/sharkey/packages/misskey-js/
cp -r packages/misskey-reversi/{built,node_modules,package.json} $out/sharkey/packages/misskey-reversi/
mkdir -p $out/sharkey/packages/frontend{,-embed}
cp -r packages/frontend/assets $out/sharkey/packages/frontend/
cp -r packages/frontend-embed/assets $out/sharkey/packages/frontend-embed/
mkdir -p $out/sharkey/tossface-emojis
cp -r tossface-emojis/dist $out/sharkey/tossface-emojis/
# create a wrapper script for running sharkey commands (ie. alias for pnpm run)
makeWrapper ${lib.getExe pnpm_9} $out/bin/sharkey \
--chdir $out/sharkey \
--add-flags run \
--set-default NODE_ENV production \
--prefix PATH : ${binPath} \
--prefix LD_LIBRARY_PATH : ${libPath}
runHook postInstall
'';
fixupPhase = ''
runHook preFixup
# cleanup dangling symlinks in node_modules, referencing workspace packages we don't include in the final output
rm -r $out/sharkey/node_modules/.pnpm/node_modules/{sw,misskey-js-type-generator,frontend-shared}
# remove packageManager line from package.json; tries to download a different pnpm version into $HOME otherwise
sed -i -e '9d' $out/sharkey/package.json
runHook postFixup
'';
passthru.tests.sharkey = nixosTests.sharkey;
meta = {
description = "Sharkish microblogging platform";
homepage = "https://joinsharkey.org";
changelog = "https://activitypub.software/TransFem-org/Sharkey/-/releases/${finalAttrs.version}";
license = lib.licenses.agpl3Only;
platforms = with lib.platforms; linux ++ darwin;
mainProgram = "sharkey";
maintainers = with lib.maintainers; [ srxl ];
};
})