2024-04-21 15:54:59 +00:00
{ config , pkgs , lib , . . . }:
let
settingsFormat = pkgs . formats . yaml { } ;
upperConfig = config ;
cfg = config . services . mautrix-meta ;
upperCfg = cfg ;
fullDataDir = cfg : " / v a r / l i b / ${ cfg . dataDir } " ;
settingsFile = cfg : " ${ fullDataDir cfg } / c o n f i g . y a m l " ;
settingsFileUnsubstituted = cfg : settingsFormat . generate " m a u t r i x - m e t a - c o n f i g . y a m l " cfg . settings ;
metaName = name : " m a u t r i x - m e t a - ${ name } " ;
enabledInstances = lib . filterAttrs ( name : config : config . enable ) config . services . mautrix-meta . instances ;
registerToSynapseInstances = lib . filterAttrs ( name : config : config . enable && config . registerToSynapse ) config . services . mautrix-meta . instances ;
in {
options = {
services . mautrix-meta = {
package = lib . mkPackageOption pkgs " m a u t r i x - m e t a " { } ;
instances = lib . mkOption {
type = lib . types . attrsOf ( lib . types . submodule ( { config , name , . . . }: {
options = {
enable = lib . mkEnableOption " M a u t r i x - M e t a , a M a t r i x < - > F a c e b o o k a n d M a t r i x < - > I n s t a g r a m h y b r i d p u p p e t i n g / r e l a y b o t b r i d g e " ;
dataDir = lib . mkOption {
type = lib . types . str ;
default = metaName name ;
description = ''
Path to the directory with database , registration , and other data for the bridge service .
This path is relative to ` /var/lib ` , it cannot start with ` . . / ` ( it cannot be outside of ` /var/lib ` ) .
'' ;
} ;
registrationFile = lib . mkOption {
type = lib . types . path ;
readOnly = true ;
description = ''
Path to the yaml registration file of the appservice .
'' ;
} ;
registerToSynapse = lib . mkOption {
type = lib . types . bool ;
default = true ;
description = ''
Whether to add registration file to ` services . matrix-synapse . settings . app_service_config_files ` and
make Synapse wait for registration service .
'' ;
} ;
settings = lib . mkOption rec {
apply = lib . recursiveUpdate default ;
inherit ( settingsFormat ) type ;
default = {
homeserver = {
software = " s t a n d a r d " ;
domain = " " ;
address = " " ;
} ;
appservice = {
id = " " ;
bot = {
username = " " ;
} ;
hostname = " l o c a l h o s t " ;
port = 29319 ;
address = " h t t p : / / ${ config . settings . appservice . hostname } : ${ toString config . settings . appservice . port } " ;
} ;
bridge = {
2024-10-04 16:56:33 +00:00
permissions = { } ;
} ;
2024-04-21 15:54:59 +00:00
2024-10-04 16:56:33 +00:00
database = {
type = " s q l i t e 3 - f k - w a l " ;
uri = " f i l e : ${ fullDataDir config } / m a u t r i x - m e t a . d b ? _ t x l o c k = i m m e d i a t e " ;
} ;
2024-04-21 15:54:59 +00:00
2024-10-04 16:56:33 +00:00
# Enable encryption by default to make the bridge more secure
encryption = {
allow = true ;
default = true ;
require = true ;
# Recommended options from mautrix documentation
# for additional security.
delete_keys = {
dont_store_outbound = true ;
ratchet_on_decrypt = true ;
delete_fully_used_on_decrypt = true ;
delete_prev_on_new_session = true ;
delete_on_device_delete = true ;
periodically_delete_expired = true ;
delete_outdated_inbound = true ;
2024-04-21 15:54:59 +00:00
} ;
2024-10-04 16:56:33 +00:00
# TODO: This effectively disables encryption. But this is the value provided when a <0.4 config is migrated. Changing it will corrupt the database.
# https://github.com/mautrix/meta/blob/f5440b05aac125b4c95b1af85635a717cbc6dd0e/cmd/mautrix-meta/legacymigrate.go#L24
# If you wish to encrypt the local database you should set this to an environment variable substitution and reset the bridge or somehow migrate the DB.
pickle_key = " m a u t r i x . b r i d g e . e 2 e e " ;
verification_levels = {
receive = " c r o s s - s i g n e d - t o f u " ;
send = " c r o s s - s i g n e d - t o f u " ;
share = " c r o s s - s i g n e d - t o f u " ;
} ;
2024-04-21 15:54:59 +00:00
} ;
logging = {
min_level = " i n f o " ;
writers = lib . singleton {
type = " s t d o u t " ;
format = " p r e t t y - c o l o r e d " ;
time_format = " " ;
} ;
} ;
2024-10-04 16:56:33 +00:00
network = {
mode = " " ;
} ;
2024-04-21 15:54:59 +00:00
} ;
defaultText = ''
{
homeserver = {
software = " s t a n d a r d " ;
address = " h t t p s : / / ' ' ${ config . settings . homeserver . domain } " ;
} ;
appservice = {
database = {
type = " s q l i t e 3 - f k - w a l " ;
uri = " f i l e : ' ' ${ fullDataDir config } / m a u t r i x - m e t a . d b ? _ t x l o c k = i m m e d i a t e " ;
} ;
hostname = " l o c a l h o s t " ;
port = 29319 ;
address = " h t t p : / / ' ' ${ config . settings . appservice . hostname } : ' ' ${ toString config . settings . appservice . port } " ;
} ;
bridge = {
# Require encryption by default to make the bridge more secure
encryption = {
allow = true ;
default = true ;
require = true ;
# Recommended options from mautrix documentation
# for optimal security.
delete_keys = {
dont_store_outbound = true ;
ratchet_on_decrypt = true ;
delete_fully_used_on_decrypt = true ;
delete_prev_on_new_session = true ;
delete_on_device_delete = true ;
periodically_delete_expired = true ;
delete_outdated_inbound = true ;
} ;
verification_levels = {
receive = " c r o s s - s i g n e d - t o f u " ;
send = " c r o s s - s i g n e d - t o f u " ;
share = " c r o s s - s i g n e d - t o f u " ;
} ;
} ;
} ;
logging = {
min_level = " i n f o " ;
writers = lib . singleton {
type = " s t d o u t " ;
format = " p r e t t y - c o l o r e d " ;
time_format = " " ;
} ;
} ;
} ;
'' ;
description = ''
{ file } ` config . yaml ` configuration as a Nix attribute set .
Configuration options should match those described in
[ example-config . yaml ] ( https://github.com/mautrix/meta/blob/main/example-config.yaml ) .
Secret tokens should be specified using { option } ` environmentFile `
instead
'' ;
} ;
environmentFile = lib . mkOption {
type = lib . types . nullOr lib . types . path ;
default = null ;
description = ''
File containing environment variables to substitute when copying the configuration
out of Nix store to the ` services . mautrix-meta . dataDir ` .
Can be used for storing the secrets without making them available in the Nix store .
For example , you can set ` services . mautrix-meta . settings . appservice . as_token = " $ M A U T R I X _ M E T A _ A P P S E R V I C E _ A S _ T O K E N " `
and then specify ` MAUTRIX_META_APPSERVICE_AS_TOKEN = " { t o k e n } " ` in the environment file .
This value will get substituted into the configuration file as as token .
'' ;
} ;
serviceDependencies = lib . mkOption {
type = lib . types . listOf lib . types . str ;
default =
[ config . registrationServiceUnit ] ++
( lib . lists . optional upperConfig . services . matrix-synapse . enable upperConfig . services . matrix-synapse . serviceUnit ) ++
( lib . lists . optional upperConfig . services . matrix-conduit . enable " m a t r i x - c o n d u i t . s e r v i c e " ) ++
( lib . lists . optional upperConfig . services . dendrite . enable " d e n d r i t e . s e r v i c e " ) ;
defaultText = ''
[ config . registrationServiceUnit ] ++
( lib . lists . optional upperConfig . services . matrix-synapse . enable upperConfig . services . matrix-synapse . serviceUnit ) ++
( lib . lists . optional upperConfig . services . matrix-conduit . enable " m a t r i x - c o n d u i t . s e r v i c e " ) ++
( lib . lists . optional upperConfig . services . dendrite . enable " d e n d r i t e . s e r v i c e " ) ;
'' ;
description = ''
List of Systemd services to require and wait for when starting the application service .
'' ;
} ;
serviceUnit = lib . mkOption {
type = lib . types . str ;
readOnly = true ;
description = ''
The systemd unit ( a service or a target ) for other services to depend on if they
need to be started after matrix-synapse .
This option is useful as the actual parent unit for all matrix-synapse processes
changes when configuring workers .
'' ;
} ;
registrationServiceUnit = lib . mkOption {
type = lib . types . str ;
readOnly = true ;
description = ''
The registration service that generates the registration file .
Systemd unit ( a service or a target ) for other services to depend on if they
need to be started after mautrix-meta registration service .
This option is useful as the actual parent unit for all matrix-synapse processes
changes when configuring workers .
'' ;
} ;
} ;
config = {
serviceUnit = ( metaName name ) + " . s e r v i c e " ;
registrationServiceUnit = ( metaName name ) + " - r e g i s t r a t i o n . s e r v i c e " ;
registrationFile = ( fullDataDir config ) + " / m e t a - r e g i s t r a t i o n . y a m l " ;
} ;
} ) ) ;
description = ''
Configuration of multiple ` mautrix-meta ` instances .
` services . mautrix-meta . instances . facebook ` and ` services . mautrix-meta . instances . instagram `
2024-10-04 16:56:33 +00:00
come preconfigured with network . mode , appservice . id , bot username , display name and avatar .
2024-04-21 15:54:59 +00:00
'' ;
example = ''
{
facebook = {
enable = true ;
settings = {
homeserver . domain = " e x a m p l e . c o m " ;
} ;
} ;
instagram = {
enable = true ;
settings = {
homeserver . domain = " e x a m p l e . c o m " ;
} ;
} ;
messenger = {
enable = true ;
settings = {
2024-10-04 16:56:33 +00:00
network . mode = " m e s s e n g e r " ;
2024-04-21 15:54:59 +00:00
homeserver . domain = " e x a m p l e . c o m " ;
appservice = {
id = " m e s s e n g e r " ;
bot = {
username = " m e s s e n g e r b o t " ;
displayname = " M e s s e n g e r b r i d g e b o t " ;
avatar = " m x c : / / m a u n i u m . n e t / y g t k t e Z s X n G J L J H R c h U w Y W a k " ;
} ;
} ;
} ;
} ;
}
'' ;
} ;
} ;
} ;
config = lib . mkMerge [
( lib . mkIf ( enabledInstances != { } ) {
assertions = lib . mkMerge ( lib . attrValues ( lib . mapAttrs ( name : cfg : [
{
assertion = cfg . settings . homeserver . domain != " " && cfg . settings . homeserver . address != " " ;
message = ''
The options with information about the homeserver :
` services . mautrix-meta . instances . ${ name } . settings . homeserver . domain ` and
` services . mautrix-meta . instances . ${ name } . settings . homeserver . address ` have to be set .
'' ;
}
{
2024-10-04 16:56:33 +00:00
assertion = builtins . elem cfg . settings . network . mode [ " f a c e b o o k " " f a c e b o o k - t o r " " m e s s e n g e r " " i n s t a g r a m " ] ;
2024-04-21 15:54:59 +00:00
message = ''
2024-10-04 16:56:33 +00:00
The option ` services . mautrix-meta . instances . ${ name } . settings . network . mode ` has to be set
2024-04-21 15:54:59 +00:00
to one of : facebook , facebook-tor , messenger , instagram .
This configures the mode of the bridge .
'' ;
}
{
assertion = cfg . settings . bridge . permissions != { } ;
message = ''
The option ` services . mautrix-meta . instances . ${ name } . settings . bridge . permissions ` has to be set .
'' ;
}
{
assertion = cfg . settings . appservice . id != " " ;
message = ''
The option ` services . mautrix-meta . instances . ${ name } . settings . appservice . id ` has to be set .
'' ;
}
{
assertion = cfg . settings . appservice . bot . username != " " ;
message = ''
The option ` services . mautrix-meta . instances . ${ name } . settings . appservice . bot . username ` has to be set .
'' ;
}
2024-10-04 16:56:33 +00:00
{
assertion = ! ( cfg . settings ? bridge . disable_xma ) ;
message = ''
The option ` bridge . disable_xma ` has been moved to ` network . disable_xma_always ` . Please [ migrate your configuration ] ( https://github.com/mautrix/meta/releases/tag/v0.4.0 ) . You may wish to use [ the auto-migration code ] ( https://github.com/mautrix/meta/blob/f5440b05aac125b4c95b1af85635a717cbc6dd0e/cmd/mautrix-meta/legacymigrate.go #L23) for reference.
'' ;
}
{
assertion = ! ( cfg . settings ? bridge . displayname_template ) ;
message = ''
The option ` bridge . displayname_template ` has been moved to ` network . displayname_template ` . Please [ migrate your configuration ] ( https://github.com/mautrix/meta/releases/tag/v0.4.0 ) . You may wish to use [ the auto-migration code ] ( https://github.com/mautrix/meta/blob/f5440b05aac125b4c95b1af85635a717cbc6dd0e/cmd/mautrix-meta/legacymigrate.go #L23) for reference.
'' ;
}
{
assertion = ! ( cfg . settings ? meta ) ;
message = ''
The options in ` meta ` have been moved to ` network ` . Please [ migrate your configuration ] ( https://github.com/mautrix/meta/releases/tag/v0.4.0 ) . You may wish to use [ the auto-migration code ] ( https://github.com/mautrix/meta/blob/f5440b05aac125b4c95b1af85635a717cbc6dd0e/cmd/mautrix-meta/legacymigrate.go #L23) for reference.
'' ;
}
2024-04-21 15:54:59 +00:00
] ) enabledInstances ) ) ;
users . users = lib . mapAttrs' ( name : cfg : lib . nameValuePair " m a u t r i x - m e t a - ${ name } " {
isSystemUser = true ;
group = " m a u t r i x - m e t a " ;
extraGroups = [ " m a u t r i x - m e t a - r e g i s t r a t i o n " ] ;
description = " M a u t r i x - M e t a - ${ name } b r i d g e u s e r " ;
} ) enabledInstances ;
users . groups . mautrix-meta = { } ;
users . groups . mautrix-meta-registration = {
members = lib . lists . optional config . services . matrix-synapse . enable " m a t r i x - s y n a p s e " ;
} ;
services . matrix-synapse = lib . mkIf ( config . services . matrix-synapse . enable ) ( let
registrationFiles = lib . attrValues
( lib . mapAttrs ( name : cfg : cfg . registrationFile ) registerToSynapseInstances ) ;
in {
settings . app_service_config_files = registrationFiles ;
} ) ;
systemd . services = lib . mkMerge [
{
matrix-synapse = lib . mkIf ( config . services . matrix-synapse . enable ) ( let
registrationServices = lib . attrValues
( lib . mapAttrs ( name : cfg : cfg . registrationServiceUnit ) registerToSynapseInstances ) ;
in {
wants = registrationServices ;
after = registrationServices ;
} ) ;
}
( lib . mapAttrs' ( name : cfg : lib . nameValuePair " ${ metaName name } - r e g i s t r a t i o n " {
description = " M a u t r i x - M e t a r e g i s t r a t i o n g e n e r a t i o n s e r v i c e - ${ metaName name } " ;
path = [
pkgs . yq
pkgs . envsubst
upperCfg . package
] ;
script = ''
# substitute the settings file by environment variables
# in this case read from EnvironmentFile
rm - f ' $ { settingsFile cfg } '
old_umask = $ ( umask )
umask 0177
envsubst \
- o ' $ { settingsFile cfg } ' \
- i ' $ { settingsFileUnsubstituted cfg } '
config_has_tokens = $ ( yq ' . appservice | has ( " a s _ t o k e n " ) and has ( " h s _ t o k e n " ) ' ' $ { settingsFile cfg } ' )
registration_already_exists = $ ( [ [ - f ' $ { cfg . registrationFile } ' ] ] && echo " t r u e " || echo " f a l s e " )
echo " T h e r e a r e t o k e n s i n t h e c o n f i g : $ c o n f i g _ h a s _ t o k e n s "
echo " R e g i s t r a t i o n a l r e a d y e x i s t e d : $ r e g i s t r a t i o n _ a l r e a d y _ e x i s t s "
# tokens not configured from config/environment file, and registration file
# is already generated, override tokens in config to make sure they are not lost
if [ [ $ config_has_tokens == " f a l s e " && $ registration_already_exists == " t r u e " ] ] ; then
echo " C o p y i n g a s _ t o k e n , h s _ t o k e n f r o m r e g i s t r a t i o n i n t o c o n f i g u r a t i o n "
yq - sY ' . [ 0 ] . appservice . as_token = . [ 1 ] . as_token
| . [ 0 ] . appservice . hs_token = . [ 1 ] . hs_token
| . [ 0 ] ' ' $ { settingsFile cfg } ' ' $ { cfg . registrationFile } ' \
> ' $ { settingsFile cfg } . tmp'
mv ' $ { settingsFile cfg } . tmp' ' $ { settingsFile cfg } '
fi
# make sure --generate-registration does not affect config.yaml
cp ' $ { settingsFile cfg } ' ' $ { settingsFile cfg } . tmp'
echo " G e n e r a t i n g r e g i s t r a t i o n f i l e "
mautrix-meta \
- - generate-registration \
- - config = ' $ { settingsFile cfg } . tmp' \
- - registration = ' $ { cfg . registrationFile } '
rm ' $ { settingsFile cfg } . tmp'
# no tokens configured, and new were just generated by generate registration for first time
if [ [ $ config_has_tokens == " f a l s e " && $ registration_already_exists == " f a l s e " ] ] ; then
echo " C o p y i n g n e w l y g e n e r a t e d a s _ t o k e n , h s _ t o k e n f r o m r e g i s t r a t i o n i n t o c o n f i g u r a t i o n "
yq - sY ' . [ 0 ] . appservice . as_token = . [ 1 ] . as_token
| . [ 0 ] . appservice . hs_token = . [ 1 ] . hs_token
| . [ 0 ] ' ' $ { settingsFile cfg } ' ' $ { cfg . registrationFile } ' \
> ' $ { settingsFile cfg } . tmp'
mv ' $ { settingsFile cfg } . tmp' ' $ { settingsFile cfg } '
fi
# Make sure correct tokens are in the registration file
if [ [ $ config_has_tokens == " t r u e " || $ registration_already_exists == " t r u e " ] ] ; then
echo " C o p y i n g a s _ t o k e n , h s _ t o k e n f r o m c o n f i g u r a t i o n t o t h e r e g i s t r a t i o n f i l e "
yq - sY ' . [ 1 ] . as_token = . [ 0 ] . appservice . as_token
| . [ 1 ] . hs_token = . [ 0 ] . appservice . hs_token
| . [ 1 ] ' ' $ { settingsFile cfg } ' ' $ { cfg . registrationFile } ' \
> ' $ { cfg . registrationFile } . tmp'
mv ' $ { cfg . registrationFile } . tmp' ' $ { cfg . registrationFile } '
fi
umask $ old_umask
chown : mautrix-meta-registration ' $ { cfg . registrationFile } '
chmod 640 ' $ { cfg . registrationFile } '
'' ;
serviceConfig = {
Type = " o n e s h o t " ;
UMask = 0027 ;
User = " m a u t r i x - m e t a - ${ name } " ;
Group = " m a u t r i x - m e t a " ;
SystemCallFilter = [ " @ s y s t e m - s e r v i c e " ] ;
ProtectSystem = " s t r i c t " ;
ProtectHome = true ;
ReadWritePaths = fullDataDir cfg ;
StateDirectory = cfg . dataDir ;
EnvironmentFile = cfg . environmentFile ;
} ;
restartTriggers = [ ( settingsFileUnsubstituted cfg ) ] ;
} ) enabledInstances )
( lib . mapAttrs' ( name : cfg : lib . nameValuePair " ${ metaName name } " {
description = " M a u t r i x - M e t a b r i d g e - ${ metaName name } " ;
wantedBy = [ " m u l t i - u s e r . t a r g e t " ] ;
wants = [ " n e t w o r k - o n l i n e . t a r g e t " ] ++ cfg . serviceDependencies ;
after = [ " n e t w o r k - o n l i n e . t a r g e t " ] ++ cfg . serviceDependencies ;
serviceConfig = {
Type = " s i m p l e " ;
User = " m a u t r i x - m e t a - ${ name } " ;
Group = " m a u t r i x - m e t a " ;
PrivateUsers = true ;
LockPersonality = true ;
MemoryDenyWriteExecute = true ;
NoNewPrivileges = true ;
PrivateDevices = true ;
PrivateTmp = true ;
ProtectClock = true ;
ProtectControlGroups = true ;
ProtectHome = true ;
ProtectHostname = true ;
ProtectKernelLogs = true ;
ProtectKernelModules = true ;
ProtectKernelTunables = true ;
ProtectSystem = " s t r i c t " ;
Restart = " o n - f a i l u r e " ;
RestartSec = " 3 0 s " ;
RestrictRealtime = true ;
RestrictSUIDSGID = true ;
SystemCallArchitectures = " n a t i v e " ;
SystemCallErrorNumber = " E P E R M " ;
SystemCallFilter = [ " @ s y s t e m - s e r v i c e " ] ;
UMask = 0027 ;
WorkingDirectory = fullDataDir cfg ;
ReadWritePaths = fullDataDir cfg ;
StateDirectory = cfg . dataDir ;
EnvironmentFile = cfg . environmentFile ;
ExecStart = lib . escapeShellArgs [
( lib . getExe upperCfg . package )
" - - c o n f i g = ${ settingsFile cfg } "
] ;
} ;
restartTriggers = [ ( settingsFileUnsubstituted cfg ) ] ;
} ) enabledInstances )
] ;
} )
{
services . mautrix-meta . instances = let
inherit ( lib . modules ) mkDefault ;
in {
instagram = {
settings = {
2024-10-04 16:56:33 +00:00
network . mode = mkDefault " i n s t a g r a m " ;
2024-04-21 15:54:59 +00:00
appservice = {
id = mkDefault " i n s t a g r a m " ;
port = mkDefault 29320 ;
bot = {
username = mkDefault " i n s t a g r a m b o t " ;
displayname = mkDefault " I n s t a g r a m b r i d g e b o t " ;
avatar = mkDefault " m x c : / / m a u n i u m . n e t / J x j l b Z U l C P U L E e H Z S w l e U X Q v " ;
} ;
2024-10-04 16:56:33 +00:00
username_template = mkDefault " i n s t a g r a m _ { { . } } " ;
2024-04-21 15:54:59 +00:00
} ;
} ;
} ;
facebook = {
settings = {
2024-10-04 16:56:33 +00:00
network . mode = mkDefault " f a c e b o o k " ;
2024-04-21 15:54:59 +00:00
appservice = {
id = mkDefault " f a c e b o o k " ;
port = mkDefault 29321 ;
bot = {
username = mkDefault " f a c e b o o k b o t " ;
displayname = mkDefault " F a c e b o o k b r i d g e b o t " ;
avatar = mkDefault " m x c : / / m a u n i u m . n e t / y g t k t e Z s X n G J L J H R c h U w Y W a k " ;
} ;
2024-10-04 16:56:33 +00:00
username_template = mkDefault " f a c e b o o k _ { { . } } " ;
2024-04-21 15:54:59 +00:00
} ;
} ;
} ;
} ;
}
] ;
2024-10-04 16:56:33 +00:00
meta . maintainers = with lib . maintainers ; [ ] ;
2024-04-21 15:54:59 +00:00
}