Compare commits

...
Sign in to create a new pull request.

443 commits

Author SHA1 Message Date
Oneric
4a810bb2ea media_proxy/preview: skip borked redirect for already static files
If we’re just deleting the static param, isntead of restarting
processing we can just continue as is given nothing (can) use
this param afterwards anyway.

Furthermore, this redirect was misconfigured, since current_url
will replace the host and port with the main AP domain, rather
than sticking to the media (sub)domain, thus breaking resolution
in the recommended setup.

Reported-by: flisk
Fixes: AkkomaGang/akkoma#1142
2026-07-12 00:00:00 +00:00
98402c48ef Merge pull request 'docs: fix wording in “AP Extensions” § “Local post scope”' (#1141) from bb010g/akkoma:zones/bb010g/cambiums/fix-docs into develop
Reviewed-on: AkkomaGang/akkoma#1141
2026-07-09 13:46:16 +00:00
745dc8ca23 docs: fix wording in “AP Extensions” § “Local post scope”
The “local” post scope was referred to as the “unlisted” post
scope.

Signed-off-by: Dusk Banks <me@bb010g.com>
2026-07-09 00:01:28 -07:00
Oneric
5fca2e5283 test: fix api/v1/instance/translator_languages test
Somehow, this didn’t fail in OTP25 CI
(and OTP28 CI was borked in unrelated ways).

Fixes oversight in c1e70540b4
2026-07-07 00:00:00 +00:00
Oneric
d5c2ee6023 web/ap/view/user: don't dupe collection patterns
With the preceeding two commits we can now be sure
there will always be usable values.
So no need for copy-paste error and desync-prone duping anymore.
2026-07-07 00:00:00 +00:00
Oneric
057d977f5a Ensure local users have collection addresses 2026-07-07 00:00:00 +00:00
Oneric
698f39d7de user: store outbox address
May be useful for backfilling from remotes
and will (once set) make local handling more consistent too
by avoiding need to dupe outbox URI pattern everywhere
2026-07-07 00:00:00 +00:00
Oneric
c1e70540b4 api/instance/translator_languages: only query service if enabled
The instance controller already had code for an {:enabled, false}
return, just nothing actually checked or produced this value.

This was especially noticeable when using mastodon-fe and not having a
valid API token for the default DeepL service configured. Mastodon-fe
proactively queries this endpoint and upon receiving a 403 response
the controller raised an exception leading to this exception and
call trace being spammed in logs.
2026-07-07 00:00:00 +00:00
f48125fcb4 Merge pull request 'Various fixes' (#1138) from Oneric/akkoma:varfixes-2026-06 into develop
Reviewed-on: AkkomaGang/akkoma#1138
2026-06-23 13:05:39 +00:00
Oneric
a25f0f3cf4 test/object/fetcher: avoid race condition on refetch 2026-06-20 00:00:00 +00:00
Oneric
5cff1fac24 Do not notify users unable to access post
Known implementations all intend to only tag users who are _also_ allowed to access the post,
but this is not guaranteed by ActivityPub semantics, thus recheck against the authorative
AP addressing fields.
An alternative worth considering is directly using the addressing fields to decide whom
to notify ignoring tags. However, MastoAPI later uses tags and thus this may lead to
confusing results if a recipient is addressed but not tagged and receives a notification about
being mentioned while the API claims they are not mentioned.

Addresses the receiving side of: AkkomaGang/akkoma#1135
2026-06-20 00:00:00 +00:00
Oneric
ac16083e99 object/fetcher: replace Create for rediscovered pruned objects
We do not generally allow updating all fields for live objects
and we in particular rely on consistency between the Create and
content object for e.g. addressing.

Updatng the old Create instead of recreating it would be possible,
but needs more new and somewhat duplicated logic. Furthermore,
reusing the old Create leads to user-visible differences wrt to
e.g. timeline ordering, but whether or not a prune was performed fully
or incomplete probably shouldn’t affect behaviour.
2026-06-20 00:00:00 +00:00
Oneric
617dd66ae4 web/activity_pub: fix recipient actor default
We want an array of string AP IDs,
but if no actor was present we got an array
containing another, empty array as an element.
2026-06-20 00:00:00 +00:00
Oneric
0baa517091 object/fetcher: apply MRFs to object reinjections
Until now fetched updates /e.g. initiated for polls) and
fetched revivals were allowed to bypass all MRF moderation.
2026-06-20 00:00:00 +00:00
Oneric
c9ff5ac9cd object/fetcher: use shared update logic
Duplicating what is an isn't updateable is just
prone to errors and creates inconsistencies.
2026-06-20 00:00:00 +00:00
Oneric
718fb684b8 object/updater: fix polls not actually being updated
Since both keys are present, this ended up effectively ignoring
poll vote changes when receiving an Update activity for the poll
2026-06-20 00:00:00 +00:00
Oneric
9fb1276aa9 cosmetic/constants: add future hint about mutable addressing 2026-06-20 00:00:00 +00:00
Oneric
814cfde9a3 object/fetcher: account for fetching display URLs
Before when attempting to fetch/lookup an already locally known post
by one of its display URLs, it always just failed on attempting to
insert a duped AP id.

This is not necessary for user fetches since they already
upsert anyway (and do not need as much extra care as objects).
2026-06-20 00:00:00 +00:00
Oneric
bc45ee7a57 object/fetcher: assert consistency when reinjecting newly fetched data
Changing id or type can create nonsensical, broken db states
and generally make little sense anyway.

Updating addressing can make sense in principle, but since we keep no
indication of whether addressing was changed by moderation action or
not and because it (mostly) wouldn't work anyway atm, just copy the old
addressing to stay consistent.
2026-06-20 00:00:00 +00:00
Oneric
73d60fb8a9 object/fetcher: merge reinjection routes again
The route for Question was split out in
ad867ccfa1
to add an ObjectValidator call to fix a bug with emojis.
Validating refetch objects to still be processable by us and genreally
valid _always_ makes sense though. The only reason this was only
reported to happen for polls (QUestion) is probably that in practice we
almost never refetch and reinject any other object type atm.

Furthermore d9a21e4784 proceeded
to remove the Transmogrifier.fix_object call from the Question path.
No reason for this was given in the commit message nor in the merge
request (originally GitLab pleroma!2915, now found at:
  https://git.pleroma.social/pleroma/pleroma/pulls/6293 ).
This too makes no sense. We _always_ want to fixup things for interop
and normalise fields. THe validators often rely on transmogrifier
pre-applying some generic normalisation and the transmogrifier is the
only place to e.g. normalise public addressing.

Thus, merge the two routes back into one applying
both of the unique extra normlisation/validation steps.
2026-06-20 00:00:00 +00:00
Oneric
a51fe2836c transmogrifier: also normalise public address via fix_object
THe fix_object route is called from some places outside transmogrifier
for objects not delivered to our inbox and also for some nested
objects inside transmogrifier.
Naturally, here too we need to normalise public addressing to
not break later.
2026-06-20 00:00:00 +00:00
Oneric
bc2233fb36 Don't cleanup redrafted attachments
The old logic considered the same file being referenced in different
attachment objects, but not the attachment object being reused in
several or a redrafted status.
Thus after a redraft, upon finding only one attachment object
referencing the file the cleanup worker falsely concluded it must
be orphaned and from the delted post continuing to clean it all up.

In practice this was hidden by the (short) delay before cleanup
and the (very long lived) nginx caches used in our setup template.

Fixes: AkkomaGang/akkoma#1137
Fixes: AkkomaGang/akkoma#1106
Fixes: AkkomaGang/akkoma#775 (again)

Complements: e8bf4422ff
2026-06-20 00:00:00 +00:00
Oneric
366a52cdee api/masto/account: fix follow* count condition
Comparing user structs directly is unreliable due to
e.g. different preload states. This lead to users in practice
sometimes not seeing their own follow* counts if hidden for others.
2026-06-20 00:00:00 +00:00
Oneric
f5bb583731 Drop C2S read on follow* collections
Not terribly useful with C2S support already being fully gone for writing
and mostly gone for reading due to beng unmaintainable in current processing setup.
This here too fixes a long-standing bug with follow* collections being
freely fetchable even if auth-fetch is turned on.

THis leaves C2S support at only reading ones own inbox.

Fixes: AkkomaGang/akkoma#729
2026-06-20 00:00:00 +00:00
Oneric
caaf7d9749 fed/in: don't accept signatures from deleted or deactivated users
In case old signing keys leaked.
And we don't want to federate with moderation-deactivated accounts anyway iinm
2026-06-20 00:00:00 +00:00
Oneric
2e0e9f8afb ap/builder: read featured address from user data
Instead of duplicating the URI generation logic
with a hard-coded format
2026-06-16 00:00:00 +00:00
Oneric
a48cdd0bf3 user: split initialising functions from property access 2026-06-16 00:00:00 +00:00
Oneric
7bacd5c9c6 user: do not hallucinate follower addresses for remote users
This absolutely makes no sense and I have no idea why this was
introduced as part ofthe initial AP support in
ae1ec858f4.

It built a _local_ follower-address URL using the (at least expanded to
include the real WebFinger or AP host) remote user’s nickname.
If the remote users doesn’t have a follower address, we’ll never
encounter anything where it would be reelvant and if we did the remote
most certainly won't use this arbitrary format using and then also
_our_ AP domain as the base of the URL.
2026-06-16 00:00:00 +00:00
1fd842a163 docs: update GET /api/v1/pleroma/admin/users fields
Reamed in 2.4.0 (2021-08-01) and mentioned in admin-fe’s CHANGELOG.md,
but updating API docs was appareantly forgotten.

Related to the renames performed in e.g.
860b5c7804.
2026-06-13 00:00:00 +00:00
Oneric
678f6fdb78 fed/in: align question and note validator fixes
The question fixups were lacking several necessary interop
adjustments failing e.g. to parse a Question with a
single hashtag not wrapped into an array.
2026-05-31 00:00:00 +00:00
899817c7f4 Merge pull request 'Update vanilla Pleroma-FE links after forge migration' (#1133) from phnt/akkoma:pleroma-fe-vanilla-links into develop
Reviewed-on: AkkomaGang/akkoma#1133
2026-05-30 22:07:10 +00:00
c8dee0ceed
Update vanilla Pleroma-FE links after forge migration 2026-05-30 23:22:55 +02:00
ceac69fec0 Merge pull request 'search/db: checkout connection' (#1131) from Oneric/akkoma:post-search-db-conn-checkout into develop
Reviewed-on: AkkomaGang/akkoma#1131
2026-05-29 17:50:20 +00:00
Oneric
fa67e77a8c deps: update mfm_parser
Fixes issue with negative numerical arguments
2026-05-28 00:00:00 +00:00
Oneric
1f214f94f9 docs: update search config docs
And fix formatting issues and typos
2026-05-28 00:00:00 +00:00
Oneric
54718690cc user/search: checkout connection
When no network requests are made to avoid getting stalled on
repeatedly waiting for a new conn between successive queries.
2026-05-28 00:00:00 +00:00
Oneric
2ef49c95ae cosmetic/search/db: drop unused function
Leftover from a restructure during local development
2026-05-28 00:00:00 +00:00
Oneric
fdc28f7f84 search/db: checkout connection
Besides the actual search query we also run a
few prep queries. Let’s avoid having to wait
for a new db conn each time
2026-05-28 00:00:00 +00:00
Oneric
a82d882ba7 api/masto/search: raise timeout
Each task might perform some additional, typically small prep queries
as well as elixir-side processing. Using the timeout for a single db
query for the task thus made little sense.

Add a new config option with a default value large enough to
accomodate both one long query and a network request plus a bit extra.
2026-05-28 00:00:00 +00:00
b4edcc0764 Merge pull request 'Handle reports with just actor ap id as the object' (#1123) from mkljczk/akkoma:iceshrimpnet-reports-fix- into develop
Reviewed-on: AkkomaGang/akkoma#1123
Reviewed-by: Oneric <oneric@noreply.akkoma>
2026-05-22 20:28:44 +00:00
fb392a8562 Merge pull request 'Tweak search' (#1113) from Oneric/akkoma:search-overhaul into develop
Reviewed-on: AkkomaGang/akkoma#1113
2026-05-22 20:25:09 +00:00
Oneric
924a3d9e48 changelog: add missing entry
Done via 45b3a80ec9
and 22dd47fd9f.
2026-05-22 00:00:00 +00:00
Oneric
45b3a80ec9 deps: update MFM parser
This extends our MFM support to almost match Iceshrimp.js
with teh exception of sparkle, followmouse and math.
This set should fully match current Iceshrimp.NET
though behaviour on invalid input and edgecases may differ.
2026-05-22 00:00:00 +00:00
4c5b6dfe6c fed/in: handle reports with just actor ap id as the object
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
2026-05-22 00:00:00 +00:00
Oneric
4a6ec3151a common_api/utils: almost always apply HTML sanitizer at end
In theory, there should be no issues since the BBCode parser
and Linkifyer are supposed to only output safe HTML, but lets
protect against theoretical bugs in those components allowing
injections.

Unfortunately, we cannot do this atm for plain text due to rel=me.
This could be resolved in the future with another scrubber, but
as noted above it is already supposed to be safe and plain-text
parsing has the lowest complexity.
2026-05-21 00:00:00 +00:00
Oneric
22dd47fd9f scrubber: allow more MFM directives and attributes
This matches everything supported by Iceshrimp.js
where federating as an MFM directive makes sense
(i.e. not eg $[center …] or $[ruby …]).

We will likely never support "followmouse" in akkoma-fe due to
requiring client-side scripting, not just CSS, but the properties
are now preserved if some other FE wants to support it.
2026-05-21 00:00:00 +00:00
Oneric
a68fb63880 deps: upgrade mfm_parser
This makes the parser more robust against HTML injection.
There was no exploit possible before either however, due to
generated HTML always passing through our sanitiser after parsing anyway.
2026-05-21 00:00:00 +00:00
Oneric
aad61de953 test: ensure MFM cannot inject HTML 2026-05-21 00:00:00 +00:00
Oneric
1b4016c71a docs/admin/cli/db: drop removed mix task
The bump_all_conversations task was only useful when support for
conversations were initially added and this task was dropped in
370d422a1d.

Fixes: AkkomaGang/akkoma#1125
2026-05-19 00:00:00 +00:00
22e3792ddb Merge pull request 'federate votersCount' (#1116) from Yonle/akkoma:fed_votersCount into develop
Reviewed-on: AkkomaGang/akkoma#1116
2026-05-09 21:56:10 +00:00
Oneric
5b05ab84f6 user/search: break tie on equal search rank
By preferring local accounts and then accounts
the server knows about for longer.

I considered using last_status_at to prefer accounts
with more recent publicly visible activity, but this
might make pagination iffy when new statuses are posted
in between API calls.
2026-05-09 00:00:00 +00:00
e13f025e22 fed/{in,out}: federate voter count for polls
This will also fix non-sensical values being reported in our API
for remote multi-selection polls, since we now actually know the
authorative voter count.

This was inspired by
5aa3c8a06e
and tests are mostly taken from
727e9e7749
but tweaked to be more accurate, remove redundancy and Akkoma API.

However, these Pleroma commits were incomplete and buggy, e.g.
miscalculating even local voter counts, missing a JSON-LD definition
and more, thus this is a greatly reworked version.

Conceptually we should also decrement the voter count when a vote is
retracted, however we currently do not handle vote retractions at all
and thus this is omitted here.

Fixes: AkkomaGang/akkoma#485

Co-authored-by: nicole mikołajczyk <git@mkljczk.pl>
Co-authored-by: Lain Soykaf <lain@lain.com>
Co-authored-by: Oneric <oneric@oneric.stub>
2026-05-09 00:00:00 +00:00
Oneric
b059948d52 db: drop redundant nickname index
All remaining nickname filters have been converted to use explicit casefolding.
If no issues show up, we can also convert the column type to regular text later
2026-05-09 00:00:00 +00:00
Oneric
698943a6b2 mix/notification_settings: migrate to User.Query helper 2026-05-09 00:00:00 +00:00
Oneric
9976b7a1cf user/query: fix escaping of literals in LIKE patterns 2026-05-09 00:00:00 +00:00
Oneric
7bef5b0dba user/query: split exact, suffix and substr nickname match
It is confusing for the single-value form of the nickname constraint
to act differently from the list version.
Furthermore all usages actually expecting a substring match via
the :nickname key wanted to match specifically a suffix and
might have processed unintended extra results before.
E.g. when collecting results for @funny.tld it would have also
matched users on funny.tld.otherinstance.example.

Exact matches can also be performed more efficiently
than either of the other two.
2026-05-09 00:00:00 +00:00
Oneric
0a63a1f71d user/query: rename :query criteria for clarity
It is only used by admin API search
2026-05-09 00:00:00 +00:00
Oneric
35a69df912 user/search: rework user search filters
The old user search query invovled several layers of nested subqueries
and the FTS conmdition and index were rather complex too. At the same
time, the manually generated ts_vector was overly permissive, allowing
entries where _any_ of the input fragments (after splitting in
preprocessing) occur, without a strong guarantee for matches containing
_all_ fragments being preferred at the end.
Furthermore, before the preceding commit it contained an akward
UNION-like via (twice) passed in array of pre-determined preferred
results. Now it still contains a two independently queried instances
of following (and a single follower) list for restricting results
if requested and always boosting search ranks of users with a follow*
relation.
Meaning both performance and result quality were quite poor.

For one particular instance, as reported in
AkkomaGang/akkoma#1112 (comment)
this lead to user search queries taking 500ms(!) on average making it
_the_ individually most costly query, taking about 20× longer than
the second worst, status full-text search. User search also took
10% of the total DB time during a full week eventhough there were
only 31 queries.

This new approach splits the index and condition logic for
nicknames and display names, using FTS only for the latter.
This results individually simpler conditions, allowing stricter criteria
for nickname matches and an overall simpler query.
Despite using now two indexes, the combined size of these new indexes
is actually smaller than the old one (at least on the instance used
for initial testing).
In principle the new nickname index is logically redundant with the
new, explicitly casefolded index since the column type "citext" is
already case-insenstitive. However, the planner cannot pick up either
version with/woithout an explicit CASEFOLD in the query and the
starts_with function doe not honour citext’s case-insensitivity.
A future commit might resolve this duplication by migrating everything
to explicit case-folding.

Search terms explicitly marked as nicknames by a leading @
will now _only_ match on the nickname column, increasing result
relevancy and sppeding the query up. This can be used e.g. for
mention autocomplete suggestions.
Our akkoma-fe frontend adopted this as part of
AkkomaGang/akkoma-fe#507

One more opinioted change in this commit is the removal of bossted
search ranks for users with follow* relations. This seemed more like
a crutch to alleviate the general poor results. When using search to
discover an account, eiother the account is well-known and the query
already pretty exact, or the account is not well-known and boosting
somewhat similar well-known accounts defeats the point.
For mention autocomplete-suggestions it might make more sense, but
the new explicit nickname path should already improve this and
currently akkoma-fe does not use the backends ordering for this anyway.
Furthermore, as it was implemented before it forced a subquery layer
degrading performance and it will always necessitate passing,
potentially very large follow* lists from the database to elixir
and back, adding more overhead.

Other than this, the actual ranking of results meeting the initial
filters is not touched in this commit (yet?). Let’s first see if the
striciter initial filters are enough to let the ranking work sensibly.
(Considered alternatives involve either ts_rank_cd which appears to
 scale differently from the trigramm ranking making it hard to combine
 both meaningfully, a set of somewhat arbitrary heuristics or both.
 The current ranking logic is more straightforward at least and
 also appears somewhat sensible already.)

Comparing execution times of the old and new fuzzy queries on a couple
inputs on a copy of db of the same instance reporting ~500ms average
execution time atm, but on a different, faster machine.
The comparsions is done without any block or follow restrictions,
nor the recently removed top_user_ids on the old version.

 search input  |  old [ms]  |  new [ms]
 --------------|------------|-----------
     "ONeriC"  |    0.552   |   9.117
          "a"  |  293.303   |  40.083
   "John Doe"  |   37.579   |   0.323
   "Jane Doe"  |    7.108   |   0.238

The degradation for the first input is the result of a planner mishap
from too coarse estiamtes / statistics. It expects way more rows to
match the FTS filter than actually do and thus decides a parallel scan
of the is_active filter to AND the (expected large) result set later
would be a good idea. Since the actual FTS result set is small though,
now almost all time is spent on the is_active index scan.
For queries with more than one word, the planner’s estimate of FTS
matches is more accurate and no such parallel is_active scan is
performed.
This may be improved once we rework user deletes and actually clean up
deleted users. Or perhaps it might turn out the is_active index is not
needed at all; possible usecases atm might involve the user index
endpoint listing all users (though it might prefer a last_status
scan anyway) and improving the active user estimate in our telemetry
statistics.

Even with this planner mishap though, the new query clearly performs
better in the "worst case" of very short search terms (commonly
encountered in searches for mention autocomplete suggestions)
and due to the different ts_vector also searches with multiple words.
It’s always much better than the previously reported average
performance and never atrociously bad.

Furthermore, for first-character automcplete suggestions this mishap
can now be avoided (if the client cooperates) through the
exxplicit-nickname alternative path.
2026-05-09 00:00:00 +00:00
Oneric
65f1a73ffe api/search/user: short-circuit on exact matches 2026-05-09 00:00:00 +00:00
Oneric
83f1f3175f notification: optimise domain block
Just as in the preceeding commit,
split_part is about 100× more efficient
2026-05-09 00:00:00 +00:00
Oneric
c43f2dfbc8 user/search: optimise domain blocks
Most our other domain extractions in SQL use split_part.
Indeed testing shows split_part is almost ten times faster
than the regex substring, so let’s adopt it here too.

Running each variant on a real-owrld user table
took ~340ms with substring and ~35ms with split_part
2026-05-09 00:00:00 +00:00
Oneric
c4f6338f8d search/database: also match against content warning text
Both "summary" and "content" are HTML fragments.
For the latter we often also have source.content
as an alternative with the original (often plaintext) markup.
However, PostgreSQL’s to_tsvector already strips out HTML tags
with their attributes.
It however will not know about e.g. MFM markup or BBCode.
Thus it’s actually _better_ to use the HTML form here.
2026-05-09 00:00:00 +00:00
Oneric
b95d3fe872 optional_migration/rum: dedupe GIN index definition 2026-05-09 00:00:00 +00:00
Oneric
1347528638 search/{db,meili}: allow locating local post by AP id without resolve
And for the database search, don't bother with a FTS
search if we already have an exact AP id match. Locating
this was almost certainly the intent of the user query.
2026-05-09 00:00:00 +00:00
Oneric
17efec060e api/search: don't fetch remote content on later pages
Already known content is still located via URI or nick
to not shift offset positions.
2026-05-09 00:00:00 +00:00
Oneric
ca9e33ed27 api/search: add fine-grained unauth search restrictions
And fix resolve=false not being honoured in post search
2026-05-09 00:00:00 +00:00
Oneric
7925947830 activity: remove search indirection 2026-05-09 00:00:00 +00:00
Oneric
e0094521cc user: remove search indirection 2026-05-09 00:00:00 +00:00
Oneric
bd52f02dc2 db/search: change default text search config for new installs to 'simple'
Setting a language matching the dominant post and query language
allows normalising inputs to also match on slight, usually
inconsequential differences (like singular vs plural form)
and omitting common filler words without too much meaning on their own.
Though the latter can also be undesirable (in the default config)
ending up reducing e.g. "but why an apple" to just "appl".

If the language does _not_ match stop-word removal and normalisation can
utterly mangle the post text and query into uselessness. Thus, forcing
specifically English by default with possible vague search issues
and a hard to discover change mechanism necessitating a costly index
rebuild seems like a bad idea.

Instead default new installs to the language-agnostic "simple" config,
which has no stop words and performs no word normalisation. It still
strips HTML tags and attributes though. If desired search can be made
more forgiving by changing the config, but there will be no straight up
_issues_ with the default.
Since we cannot know whether the config was already customised or
intentionally set to english and to avoid forcing a costly index
rebuild, existing installs will keep their current setting.
2026-05-09 00:00:00 +00:00
Oneric
0c64c5844c dbsearch: use two-arg websearch_to_tsquery version
We already queried the text search config anyway for GIN and
the version with an explicit config is an immutable function
which ensures the query planner doesn’t do silly things and caches
the evaluation result.
(PostgreSQL 16 seems to execute even the one-arg version only once)

This might help with the repeated reevaluation problem reported in
AkkomaGang/akkoma#650. Note, another even more
foolproof way to ensure only a single eval happens is using a cross join
with the websearch_to_tsquery result. However this can incur significant
overhead for performing a join operation. Since PostgreSQL 13 [1], the
planner is smart enough to re-inline cross joins with immutable
functions, but before that both to_tsvector variants suffer from this
and as of PostgreSQL 16 the one-arg version still does.

Maybe fixes: AkkomaGang/akkoma#650
2026-05-09 00:00:00 +00:00
Oneric
6e0e20644b user/doc: mention get_cached_by_nickname falls back to network lookup
By itself the name implies only local lookups are performed.
get_cached_by_ap_id does NOT perform network lookups.
(And get_cached_by_id also not, although it too does a weird extra indirection through ap_id)
2026-05-09 00:00:00 +00:00
Oneric
c92456bf78 Hide OAuth tokens from logs
Else, when sharing debug-level logs
some admins may unintentionally leak
still valid user tokens or authorizations
2026-05-06 19:43:51 +00:00
Oneric
32826f4806 web/o_auth: drop unused module
It has been empty and unused since
799e1f48b5
2026-05-06 19:43:51 +00:00
Oneric
0ed9a8de67 telemetry: expose count of failed jobs waiting for a retry 2026-05-06 19:43:51 +00:00
a
04f3b39363 nginx: fix broken http->https redirect
The HTTP server block deals with _both_ the main and media domain,
but $server_name only ever takes the value of the first listed domain.
Using $host instead allows both to work.
2026-05-06 00:00:00 +00:00
Oneric
c0687dc395 changelog: add mix version number too 2026-05-05 00:00:00 +00:00
Oneric
1a8fe6081d changelog: fix typo 2026-05-05 00:00:00 +00:00
Oneric
5d3c43c49e static/images/banner: use fully transparent default
This matches the visual appearance we had for
over three years before 0b049c3621
retored the previous, grey default banner.

The grey default often clashes with custom themes
making text hard to read or just looking out of place.
Since we must provide something per Masto API though,
let’ſ sjust use a fully transparent image.
2026-05-05 00:00:00 +00:00
e4ed44ba29 update changelog 2026-05-04 20:03:30 +01:00
d6df0dd7d9 update httpsignatures 2026-05-04 19:57:43 +01:00
26cb389528 probably best to just not match the not-present case 2026-05-02 18:55:34 +01:00
c50afa78d2 account for multiple host headers, add test for non-default port 2026-05-02 18:54:28 +01:00
c397d29930 remove weird unicode artifact 2026-05-02 16:38:30 +01:00
a5f4cabf14 assert on header itself, ensure host check has been run 2026-05-02 16:33:45 +01:00
c74b0ac96e add unit tests for host header checking 2026-04-30 17:47:45 +01:00
fb37688dd9 mix format 2026-04-30 17:30:47 +01:00
a92e97277b enforce host header matching
on all routes that can be sent via the HTTP signature check, adds
an additional check for the host header. will error if there
is no host header, or if it does not match our configured endpoint
2026-04-30 17:29:00 +01:00
Oneric
1804337593 docs/admin/monitoring: fix typesetting and link 2026-04-21 00:00:00 +00:00
Oneric
bc4699059f web/manifest: fix icons not being user-configurable
The docuemnted config options only worked for the MastoFE version.
While at it, just allow all keys to be overriden by admins and
merge logic for both manifest variants.

Reported-by: Clovis <clovis@bdx.town>
2026-04-20 00:00:00 +00:00
4e07d28db8 Merge pull request 'Determinsitic API results' (#1108) from Oneric/akkoma:deterministic-api-results into develop
Reviewed-on: AkkomaGang/akkoma#1108
2026-04-20 22:04:59 +00:00
Oneric
0be230eae8 mix: update ecto and postgrex dependencies
telemetry is automatically pulled in too
2026-04-20 00:00:00 +00:00
Oneric
811f0a4682 Fix non-deterministic API results
The db setting for fuzzy GIN scans was added in
b6bb73f43e with the intention of
optimising full-text searches of posts and users at the cost of a
slight inaccuracy in results for edge cases of very common queries.
Indeed, this is the intended usecase of the setting as per its docs¹.
In practice however, it affects more than just FTS
and the consequences are much more jarring.

The setting affects _all_ GIN index scans, not just FTS-typed columns
specifically. Besides the post and user FTS indexes, our database
currently uses GIN indexes in the Oban table and for activities’
recipients. Thus on particularly busy servers this might screw with
job control. On all servers this will screw with results for anything
primarly scanning the recipient index. Whether this is the case depends
on the lanner and Postgres’ statistics. It most likely was the source
of incomplete and unstable results in the since removed DM timeline
(ref.: AkkomaGang/akkoma#798) and can
(due to overly coarse Postgres statistics) also happen for the
last-status query used in DM conversations.
In theory public and unauthorised timeline views may also scan
recipients for as:Public entries. In practice though, this value is so
common Postgres will most likely prefer any other sensible index or even
just perform a full table scan if there are no other restrictions.

Furthermore, even for FTS this limit already had very noticeable
and undesireable consequences. We heard from some servers about
their search results being seemingly random and changing slightly
to drastically on each reload. E.g. on akko.wtf search results
tended to add and remove a couple entries each reload for many queries.
On labyrinth.zone results often ended up mistakenly empty or only with
a (small) random subset of matching posts often completely different
between each query (with a non-empty result).
While optimising FTS is indeed the documented use-case of this setting,
the docs also recommend for a value "in the thousands" specifically
“5000 – 10000”, yet the value we used to enforce was but a tenth of this
range’s lower bound. Yet, even 10000 still exhibited some instability in
results albeit to a lesser degree than before. Thus just do not set this
by default at all. For servers which really need to limit FTS searches
for perf reasons and are willing to accept some instability in FTS
results, a new config option is provided to set this specifically
_only_ in connections performing a FTS search.

[1]: https://www.postgresql.org/docs/17/gin.html#GIN-TIPS
2026-04-20 00:00:00 +00:00
Oneric
85aa11d40e activity_pub: optimise restrict_recipients for single-element lists
At least as of PostgreSQL 18, the planner likes this more
2026-04-19 00:00:00 +00:00
Oneric
7abf2be6e9 migrations: fix removal of skip_thread_containment user setting
There was no harm in the original version wit hthe mixed up add,
but it we don't need the column so keeping it around is a waste
2026-04-14 00:00:00 +00:00
108fbb0f87 Merge pull request 'Drop deprecated stuff and leftovers from prev removals' (#1105) from Oneric/akkoma:drop-deprecated-and-awful-db-visibility-idx into develop
Reviewed-on: AkkomaGang/akkoma#1105
2026-04-14 18:36:01 +00:00
Oneric
ae35a8ad74 object/fetcher: fix error clause for MRF rejects
The reject and accept checks were merged in commit
dee0e01af9. While both
occurence of those checks (in fetch_object_from_id/2 and
fetch_and_contain_remote_ap_doc/3 respectively) were updated,
only one of the with error clauses was updated to match.

This broke the mapping and log-level adjustments for MRF-related
key fetch errors introduced in 69a2b4d149.
2026-04-11 00:00:00 +00:00
Oneric
7869030e08 api/statuses/context: fix local-only contexts
The context query is separate from the main timeline query
but was omitted to be adjusted when local-only posts were introduced.
Thus only local-only posts directly addressing the API user were considered.
2026-04-07 00:00:00 +00:00
Oneric
a088a0136b api/statuses/context: return 404 when not existing
Or is not visible to the current user.
Previously led to 500 Internal Server Error if post didn’t exist
and empty response if post existed but was not accessible.
2026-04-07 00:00:00 +00:00
Oneric
0ef2eace63 Optimise timelines for block-less users
Block related conditions are rather numerous and complex in itself,
require an extra prep query to get follower addresses and
potentially even an extra join.
This was observed to contribute to overhelming the planner in sometimes.

If there aren’t any incoming or outgoing blocks the respective extra
bits are not actually needed, thus skip them when we can.
2026-04-07 00:00:00 +00:00
Oneric
e4066ca98e Remove remnants of list addressing
The feature was removed in 271110c1a5
2026-04-07 00:00:00 +00:00
Oneric
370d422a1d db: drop activity_visibility and related indexes
The last user of this acursed mess was dropped in the preceding commit.
The COALESCE_follower_addres index existed solely for activity_visibility.

Now nothing is blocking parallel restore of db dumps anymore
allowing significantly faste restores and removing the pitfall
of (when not following our old documented flgs) greatly _worsening_
restore times by enabling parallel mode.
2026-04-07 00:00:00 +00:00
Oneric
9e105a64a9 api: drop direct timeline
As announced in the 3.18 (2025.12) release

Closes: AkkomaGang/akkoma#798
2026-04-07 00:00:00 +00:00
Oneric
d3237a9387 web/activity_pub: get last status of dm conv without activity_visibility
The db function "activity_visibility" is buggy and for example also
selects local-only posts as "direct" here. Duplicating general
visibility classification logic in both elixir and for some select
usecases a db-side function predictably resulted in both versions
drifiting apart.
Furthermore, the function is inefficient and relied on an oth an index
for computation and another index to cache its own result lading to
index interdependence which in turn creates issues during restore.
Performance-wise the cached activity_visibility result
didn’t help that much anyway here.

Thus switch away from activity_visiblity also
fixing the misclassification of local-only posts.
Unfortunately though, this still dupes visibility classification logic
into sql statements, though now only for "direct" specifically.
Future reworks may perhpas get rid off this too.

Additionally this now immediately restricts recipients to only direct mentions,
potentially trimming of a bunch of activities early before further checks
(or the join) needs to be performed.

The planner produces a more complex plan for this in my testing, but
the plan has both a better estimate and a slightly better actual
execution time (about 0.2ms execution time
dominated by the planner’s runtime of 3ms).

On the much larger akko.wtf instance the planner ends up with a
comparable plan, but this plan is both estimated to perform much worse
and actually does take longer (3ms planner time and 9ms execution time).
It is unclear why this happens, the old plan is still valid with
just a small change to the filter at one stage.
However, in absolute terms the difference is still only a few
milliseconds, while the /api/v1/conversations endpoint currently
already takes around 2s to respond. While the relative change is
concerning, in practive the absolute difference should be neglible.
2026-04-07 00:00:00 +00:00
Oneric
f81bf5214b api: remove deprecated thread containment feature
While disabled by default since almost immediately after its
introduction and entirely undocument and seemingly unsued anyway,
it was deprecated in 9d19dbab99 with
a loud warning to any potential users (see this commit for motivation).

Nobody contacted us so now it is time to follow up with a proper removal
2026-04-07 00:00:00 +00:00
601c5b107d Merge pull request 'Detect whether polls are promised to be anonymous or publicising voter identity with their cast vote' (#1104) from Oneric/akkoma:poll-anon into develop
Reviewed-on: AkkomaGang/akkoma#1104
2026-04-04 19:32:31 +00:00
ee5b6f250f Merge pull request 'Allow providing avatar/header descriptions' (#1034) from mkljczk/akkoma:avatar-header-descriptions into develop
Reviewed-on: AkkomaGang/akkoma#1034
Reviewed-by: Oneric <oneric@noreply.akkoma>
2026-04-04 12:13:45 +00:00
Oneric
17841dca53 api: finalise removal of exclude_visibilites
It was tenatively removed in 0aeeaeb973
for release 3.18 (2025.12) with the intent to follow up with a proper
clean if no objections were raised.
Nobody complained, so lets now get rid off its vestiges.
2026-04-04 00:00:00 +00:00
Oneric
6ed20f1ca3 fed/out: indicate our own polls are always anonymous
With regard to regular user and admin interaction via API.
Ofc, the server operator can still extract identites from the database.
2026-04-04 00:00:00 +00:00
dc3673fffa api/account: add profile media alt text
Projects like e.g. Iceshrimp.NET and Pleroma already allow
setting and federate alt text for profile media.
Mastodon too recently added support for avatar and header descriptions
in https://github.com/mastodon/mastodon/pull/37634
and https://github.com/mastodon/mastodon/pull/38221.

We use Mastodon-compatible API parameters for avatar and headers and
extend the naming scheme for background images not found in Mastodon.
There’s one difference however: we do not actually store media alt text
when no matching media was yet set at all. Since media is stored as an
inline, AP-like JSON object, it can only exist if there’s an image.

Co-authored-by: Oneric <oneric@oneric.stub>
2026-04-04 00:00:00 +00:00
Oneric
11982eb249 {api,fed/in}: expose remotes claims wrt poll vote anonymitiy
Most of the major micro-blogging AP implementations do not make
votes or voter identity available to anyone by regular means.
However, this is but an informally adopted common practice.

Smithereen may (depending on the decision of the creating user)
disclose who voted and what everyone voted for. This information
is made publically available to everyone, including via ActivityPub
(eventhough the AP vote collections show some type and data
 inconsistencies between the inline and standalone version at the
 time of writing. It is necessary to fetch the standalone collections
 for the full information.)

Smithereen does indicate whether a poll will disclose votes and voter
identies and when this is kept secret. But of course, for this info
to be visible to our users we will need to first pick up the hint
from Smithereen and forward it in our Masto API responses.

Example: https://friends.grishka.me/posts/1116518
2026-04-04 00:00:00 +00:00
1f268809df Merge pull request 'Use magick command from ImageMagick' (#1101) from norm/akkoma:magick into develop
Reviewed-on: AkkomaGang/akkoma#1101
2026-04-02 20:42:08 +00:00
a14a369a3f media_helper: allow fallback to ImageMagick 6 cmmands
Make `check_filter` more flexible by checking a list instead of a single
command, and also use the `mogrify` command for the Mogrify filters, as
it's not deprecated.

This allows keeping compatability with ImageMagick v6 (which is itself
still supported for several years) while avoiding warning spam on v7.
2026-04-02 00:00:00 +00:00
5e04692be2 media_helpers: replace commands deprecated in ImageMagick 7
With ImageMagick version 7 the convert command has been deprecated in
favour of magick. Calling convert instead results in the logs being
spammed with warning messages.
2026-04-02 00:00:00 +00:00
ea9d1f1205 correct changelog 2026-04-01 11:39:13 +01:00
80fc784432 Merge pull request 'Add sane defaults for database_config_whitelist, add a task to remove non-whitelisted configs' (#1077) from mkljczk/akkoma:akkoma-database-config-whitelist into develop
Reviewed-on: AkkomaGang/akkoma#1077
Reviewed-by: floatingghost <hannah@coffee-and-dreams.uk>
2026-04-01 10:38:29 +00:00
Oneric
0b061090ac ap/transmogrifier: fix attachment MIME extraction
If an attachment with a full Link object as an "url" (i.e. a map)
but no "mediaType" attribute in the Link made it to this point
processing crashed since MIME.extensions/1 requires binary input.

Additionally the is_bitstring/1 checks in the other branches were too
lax; bitstrings may contain any number of bits not just full bytes.
2026-03-30 00:00:00 +00:00
Oneric
e72fdd08b4 list: simplify exclusive member query
Ecto does not like directly seecting `r` since it was defined as a
fragment. Using array_agg worked around this. However, even just a
trivial fragment is sufficient here allowing a slightly simpler query.

The estimated plan cost barely differs and no noticeable real-world
execution time difference was observed, but the new version is at least
easier to read.
2026-03-29 00:00:00 +00:00
58d0aee027 Merge pull request 'web: mark local uploader and proxied responses as immutable' (#1096) from Oneric/akkoma:cachecontrol-yonle into develop
Reviewed-on: AkkomaGang/akkoma#1096
2026-03-28 23:46:42 +00:00
Oneric
4af15ed633 ci/docs: move to ARM runner
It is less used, faster and unlike OTP builds
docs don’t need to be on any particular architecture.
2026-03-28 00:00:00 +00:00
48eaf69a06 web: mark local uploader and proxied responses as immutable
While modern browsers already limit revalidation requests a lot,
marking it as immutable enusres there’ll never be a need for
revalidation until the max-age (currently two weeks) elapses.

Uploaded media is indeed guaranteed to be immutable with
the hash of the file content being part of the path.
Proxied responses are technically not immutable, but we’d
like to not reproxy it if we can avoid doing so. Reverse
proxy (e.g. nginx) side caching already helps here and
may overwrite this flag if desired.
2026-03-28 00:00:00 +00:00
Oneric
570de73062 fed/in: reject inbox POST with 405 if not federating
As advised by ActivityPub spec.
2026-03-28 00:00:00 +00:00
Oneric
455ea15817 fed/out: recognise if inbox does not support federation 2026-03-28 00:00:00 +00:00
a8006e5d88 Merge pull request 'Support lists exclusive param' (#1062) from mkljczk/akkoma:exclusive-lists- into develop
Reviewed-on: AkkomaGang/akkoma#1062
2026-03-28 13:12:42 +00:00
Oneric
e761960fcc api/masto/list: include reblogs in timelines
This matches Mastodon’s behaviour.

The test for the absence of reblogs was originally added without
explanation in cfc99fe05c.
2026-03-28 00:00:00 +00:00
f268e63b60 api/masto/list: support exclusive param
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
2026-03-28 00:00:00 +00:00
34065495bc Merge pull request 'changelog + docs: add notice re database analyze-ing following migrations' (#1093) from kouett/akkoma:fix/2026-03-db-migration-documentation into develop
Reviewed-on: AkkomaGang/akkoma#1093
2026-03-27 20:07:37 +00:00
Oneric
2a97a08b9d doc/install/fronted: highlight the need for matching MIX_ENV
Resolves: AkkomaGang/akkoma#1073
2026-03-27 00:00:00 +00:00
Oneric
a3a08a08b2 doc/admin/updating: mention how to refresh planner stats
Sometimes changes aren’t picked up automatically,
so document an easy way to force it.

We don’t want to force an ANALYZE on each migration since this
just wastes time on new installs and for those whose planner
already picks it up anyway.
2026-03-27 00:00:00 +00:00
bb297acd32 changelog/2026.03: add note recommending ANALYZE 2026-03-27 00:00:00 +00:00
Oneric
07e2455837 docs/config/cheatsheet: fix ref link for akkoma-fe config
The advertised file no longer exists, probably since
the backend repo stopped carrying a precompiled frontend.
Also highlight options for custom commit URLs.
2026-03-26 00:00:00 +00:00
Oneric
2774286ebd mix/db/update_users_following_followers_counts: skip deleted remote users
Their collections are no longer available anyway.
2026-03-25 00:00:00 +00:00
f3b39e9ea2 Merge pull request 'user: Return triple instead of tuple as fallback' (#1090) from cve/akkoma:user-triple into develop
Reviewed-on: AkkomaGang/akkoma#1090
2026-03-24 16:04:46 +00:00
Clara Engler
792b1b8a09 user/fetcher: fix fallback return schema for eval_counter_collection
Fixes omission in d2fda68afd;
the fallback function clause was not updated to include
whether or not counters are considered private, leading
to match exceptions for users without a follower or following address.

Likely fixes AkkomaGang/akkoma#1080
2026-03-24 00:00:00 +00:00
8ecbad601a Merge pull request 'Fix octet-stream type from /embed' (#1084) from shrimple.pl/akkoma:embed into develop
Reviewed-on: AkkomaGang/akkoma#1084
Reviewed-by: Oneric <oneric@noreply.akkoma>
2026-03-23 18:08:15 +00:00
ac8c412d8d Merge pull request 'Add AKKOMA_MAX_FDS env var for setting ulimit -n' (#1079) from provable_ascent/akkoma:max-fds into develop
Reviewed-on: AkkomaGang/akkoma#1079
Reviewed-by: Oneric <oneric@noreply.akkoma>
2026-03-23 15:54:03 +00:00
9ee4283c46 web/embed: fix content-type for /embed
Currently the response uses a generic application/octet-stream type
causing browsers to just offer a download rather than actually embedding
it. Additionally this no longer usees the embedding template, but the
same app template like static-fe breaking styling.
Presumably the latter broke when the layout assignment got lost in
c63ae73bc0. Thus restore it and since
even then it is not set automatically (anymore?), explicitly set
response contnet-type to HTML.

Add unit test for correct text/html content-type so this won’t repeat.
2026-03-23 00:00:00 +00:00
Oneric
720785b4cc api/masto/marker: fix datetime format for updated_at
Ref: https://docs.joinmastodon.org/api/datetime-format/

Fixes: AkkomaGang/akkoma#1087
2026-03-23 00:00:00 +00:00
890b6e9f68 docker: add default nofile ulimits to docker-compose.yml
Add documentation section regarding OS limits and config

Resolves: AkkomaGang/akkoma#1020
2026-03-23 00:00:00 +00:00
bfc5de4131 Merge remote-tracking branch 'secret/fix-webfinger-the-second' into develop 2026-03-21 19:29:14 +00:00
70c08ae67b bump version 2026-03-21 18:44:06 +00:00
Oneric
a062ac5ceb webfinger/finger: actually limit refetch
Fixes mistake in eb361dd456
2026-03-21 00:00:00 +00:00
Oneric
ce68efa5d2 changelog: add missing entries 2026-03-21 00:00:00 +00:00
Oneric
eb361dd456 webfinger/finger: only accept authority of query domain
And permit refetching once(!) unless initial query
was already designated as the canonical authority.
(Only once to not get stuck in loops)

Fixes an oversight in c80aec05de.
The argument for why subjects from both authorities can be accepted
hinged on the assumption that only paths under direct control of the
domain operator are involved since both webfinger and host-meta
are /.well-known/ paths.
However, HTTP redirects or the LRDD schema inside the initial domains
host-meta may point at _any_ path on another domain, including e.g.
paths containing user uploads, thus enabling third-parties to
illegitimately claim handles from urelated domains, _if_ the victim
domain can be made to serve attacker-prepared JSON (e.g. via user
uploads or (media) proxies).

With trust being limited to initial domain and refetches we do not need
to make guesses about whether and when being redirected to indicates
authorisation of the final domain. It requires more fetch requests in
no-FEP-2c59 setups, but makes it more robust.
As a side effect current FEP-less Mastodon setups should happen to work.
2026-03-21 00:00:00 +00:00
Oneric
9baf4d16e5 test: properly mock url in more Tesla responses 2026-03-21 00:00:00 +00:00
5f48e1cba8 Merge pull request 'web(activity_pub/utils): temporarily deal with nil status ID when dealing with report' (#1082) from Yonle/akkoma:fixreport into develop
Reviewed-on: AkkomaGang/akkoma#1082
Reviewed-by: Oneric <oneric@noreply.akkoma>
2026-03-16 19:30:24 +00:00
a0c59c96e6
f o r m a t 2026-03-17 01:20:52 +07:00
ca647214a2
web(activity_pub/utils): temporarily deal with nil status ID when dealing with report
as previously discussed on AkkomaGang/akkoma#712 ,
there are some kind of quirk when a status data sometime has `id` being `nil`.

this patch should be a temporary bandaid for now.
2026-03-16 21:16:07 +07:00
479c72888d Merge pull request 'timeline_operation: also add pagination_params handling on hashtag_operation' (#1081) from Yonle/akkoma:fixhashtagtl into develop
Reviewed-on: AkkomaGang/akkoma#1081
2026-03-15 14:48:35 +00:00
7595f91955 Merge pull request 'api/view: handle explicitly zero dimensions in attachments' (#1085) from Oneric/akkoma:fix-zero-extent-attachment into develop
Reviewed-on: AkkomaGang/akkoma#1085
Reviewed-by: floatingghost <hannah@coffee-and-dreams.uk>
2026-03-15 01:21:17 +00:00
871fd17fbe api/timeline/hashtag: allow pagination params in spec
Otherwise they get stripped and ignored.
2026-03-15 00:00:00 +00:00
Oneric
6833281051 api/view: handle explicitly zero dimensions in attachments
Some peertube videos are federated with explicitly zero dimensions
for their mp4 link leading to a division-by-zero exception here.
E.g.: https://spectra.video/videos/watch/66c12674-2013-47a4-9126-51ff8eb8d0a8
      (as of writing using peertube 8.1.1)

Reported by temp3 on IRC
2026-03-15 00:00:00 +00:00
a913c11230 bump version 2026-03-14 13:20:10 +00:00
Oneric
9d1e169472 webfinger/finger: allow WebFinger endpoint delegation with FEP-2c59
The ban on redirects was based on a misreading of FEP-2c59’s
requirements. It is only meant to forbid addresses other than
the canonical ActivityPub ID being advertised as such in the
returned WebFinger data.
This does not meaningfully lessen security and verification still
remains stricter than without FEP-2c59.

Notably this allows Mastodon with its backwards WebFinger redirect
(redirecting from the canonical WebFinger domain to the AP domain)
to adopt FEP-2c59 without causing issues or extra effort to existing
deplyoments which already adopted the Mastodon-recommended setup.
2026-03-13 00:00:00 +00:00
Oneric
4f03a3b709 federation.md: document our WebFinger behaviour 2026-03-13 00:00:00 +00:00
Oneric
e8d6d054d0 test/webfinger/finger: fix tests
One asserted the response format of finger_actor on a finger_mention
call as a previous iteration of the implementation mistakenly returned.
The other didn’t actually test anything WebFinger but fundamental id
containment and verification for generic AP fetches. Now it does.
2026-03-12 00:00:00 +00:00
Oneric
ab2210f02d test/webfinger/finger: add more validation tests 2026-03-12 00:00:00 +00:00
Oneric
5256785b9a test/webfinger/finger: mock url in all new tests
It is used in security checks and only due to an abudance of
existing tests lacking it, _only_ the test env is allowed to
fallback to the query URL for theses tests as a temporary
(well, ... it’s been a while now) measure.
We really shouldn’t be adding more deficient tests like that.
2026-03-12 00:00:00 +00:00
Oneric
4460c9c26d test/webfinger/finger: improve new test names and comments 2026-03-12 00:00:00 +00:00
f87a2f52e1 add extra happy and unhappy path tests for webfingers 2026-03-12 00:00:00 +00:00
627ac3645a add some more webfinger tests 2026-03-12 00:00:00 +00:00
2020a2395a add baseline webfinger FEP-2c59 tests 2026-03-12 00:00:00 +00:00
Oneric
fd734c5a7b webfinger/finger: normalise mention resources to more common format 2026-03-12 00:00:00 +00:00
Oneric
d86c290c25 user: drop unused function variants and parameters 2026-03-12 00:00:00 +00:00
Oneric
838d0b0a74 ap/views: use consistent structure for root collections
Notably user follow* collections faked a zero totalItem count
rather than simply omitting the field and included a link to a first
page even when knowing this page cannot be fetched while most others
omitted it. Omitting it will spare us receiving requests doomed to
failure for this page and matches the format used by GtS and Mastodon.

Such requests occur e.g. when other *oma servers try to determine
whether follow* relationships should be publicly shown. Other
implementations like Mastodon¹ simply treat the presence of a (link to)
a first page aready as an indicator for a public collection. By
omitting the link Mastodon servers will now too honour our users
request to hide follow* details.

The "featured" user collection are typically quite small and thus
the sole occurence of the alternative form where all items are directly
inlined into the root without an intermediate page. Thus it is not
converted but also no helper for this format created.

1: eb848d082a/app/services/activitypub/process_account_service.rb (L303)
2026-03-12 00:00:00 +00:00
Oneric
ddcc1626f8 ap/user_view: optimise total count of follow* collections
The count is precomputed as a user property.
Masto API already relies on this cached value.
This let’s us skip actually querying  follow* details unless
follow(ing/ed) users are publicly exposed and thus will be served.

In fact this could now be optimised further by using keyset pagination
to only fetch what’s actually needed for the current page. This would
also completely obsolete the need for the _offset collection page
helpers. However, for this pagination to be efficient it needs to happen
o the follow relation table, not users. This is left to a future commit.

Due to an ambiguity with PhoenixHtmlHelpers the Ecto.Query
select import was unusable without extra qualification,
therfore it is converted to a require expression.
2026-03-12 00:00:00 +00:00
Oneric
fbf02025e0 user/query: drop unused legacy parameter
There deactivated db column since 860b5c7804
and users of this legacy kludge introduced in 10ff01acd9
all migrated to an equivalent newer parameter
2026-03-12 00:00:00 +00:00
Oneric
a899663ddc Fix malformed is_active qualifiers in User.Query usages
While "is_active" is a property of users, it is not a recognised keyword
for the User.Query helper; instead "deactivated" with negated value must
be used.
This arose because originally the user property was also called
"deactivated" until it was renamed adn negated five years ago
in 860b5c7804. This included renaiming
the parameter in most but not all User.Query usages.
Notably the parameter in User.get_followers_query was renamed
but not in User.get_friends_query (friends == following).
The accepted query parameter in User.Query however was not changed.
This lead to the former mistakenly including deleted users causing
too large values to be reported in the ActivityPub follower, but not
following collection as reported in #1078.

In Masto API responses filtering by `User.is_visible` weeded out
the extra accounts before they got displayed to API users.

On the surface it might seem logical to align the name of the User.Query
parameter with the actual property name. However, User.Query already
accepts an "active" parameter which is an alias for limiting to accounts
which are neither deleted nor deactivated by moderators (both indicated
by is_active) and also are not newly created account requests still
pending an admin approval (is_approved) or necessary email confirmation
(is_confirmed); in short as the alias suggests whether the account is
active. Two highly similar parameter names like this would be much too
confusing.

The renamed "is_active" on the other hand does not actually suffice to
say whether an account is actually active, only whether it has (not yet)
ceased to be active (by its own volition or moderator action).
Meaning its "new" name is actively misleading. Arguably the rename
made things worse for no reason whatsoever and should not ever have
happened.

For now, we’ll just revert the incorrect query helper parameter renames.

Fixes: https://akkoma.dev/AkkomaGang/akkoma/ issues/ 1078
2026-03-12 00:00:00 +00:00
Oneric
d2fda68afd user/fetch: properly flag remote, hidden follow* counts
Instead of treating them like a public zero count.
2026-03-12 00:00:00 +00:00
Oneric
9fb6993e1b cosmetic/user/fetch: reorder functions
Such that helper functions are near their sole caller
instead of being interpsarsed with other public functions
2026-03-12 00:00:00 +00:00
Oneric
4bae78d419 user/fetcher: assume collection to be private on fetch errors
With the follow info update now actually running after being fixed
a bunch of errors about :not_found and :forbidden collection fetches
started to be logged
2026-03-12 00:00:00 +00:00
Oneric
f3821628e3 test: fix module name of GettextCompanion tests
Oversight in 54fd8379ad
2026-03-12 00:00:00 +00:00
Oneric
b4c6a90fe8 webfinger/finger: allow leading @ in handles
At least for FEP-2c59 this shouldn’t be the case
but in theory some WebFinger implementations may
serve their subject with an extra @
2026-03-12 00:00:00 +00:00
Oneric
a37d60d741 changelog: add missing changes 2026-03-12 00:00:00 +00:00
Oneric
71757f6562 user/fetcher: utilise existing verified nick on user refetch
Unless the force-revalidation config is enabled (currently the default).
Also avoids an unneecessarily duplicated db query for the old user.
2026-03-12 00:00:00 +00:00
Oneric
892628d16d user/fetcher: always detect nickname changes on Update activities
Even when the "always force revalidation" option is not enabled
while avoiding unnecessary revalidations if nothing changed.

With this heuristic we should be able to change the default to "false"
soon, but for now keep it enabled to help amend recent bugs.
2026-03-12 00:00:00 +00:00
Oneric
2a1b6e2873 user/fetcher: drop nonsense type-based follow update skip
The real intent behind the commit introducing this seemed to have been
avoiding running this when the actor does not expose follow collection ids
ec24c70db8.
This is already taken care of with the :collections_available check.
Some Implementations use other actor type like Group etc for visible,
followable actors making skipping undesirable.

Notably though, this actually has _always_ skipped counter updates
as even when this check was introduced, the user changeset data and
struct used the :actor_type key not :type.

In some situations fetch_follow_information_for_user is called directly
from other modules thus occasionally counters still got updated
for accounts with closer federation relationships masking the issue.
2026-03-12 00:00:00 +00:00
Oneric
756cfb6798 user/fetcher: fix follow count update
Users no longer have an info substruct for over 6 years
since e8843974cb.
Instead the counters are now directly part of the user struct itself.
2026-03-12 00:00:00 +00:00
Oneric
698ee181b4 webfinger/finger: fix error return format for invalid XML
By default just a plain :error atom is returned,
differing from the return spec of the JSON version
2026-03-12 00:00:00 +00:00
Oneric
c80aec05de webfinger: rewrite finger query and validation from and to actors
Resolves all security issues discussed in 5a120417fd86bbd8d1dd1ab720b24ba02c879f09
and thus reactivates skipped tests.
Since the necessary logic significantly differs for WebFinger handle dicovery/validation
and fetching of actors from just the webfinger handle the relevant public function was split
necessitating also a partial rewrite of the user fetch logic.

This works with all of the following:
  - ActivityPub domain is identical to WebFinger handle domain
  - AP domain set up host-meta LRDD link to WebFinger domain
  - AP domain set up a HTTP redirect on /.well-known/webfinger
    to the WebFinger domain
  - Mastodon style: WebFinger domain set up a HTTP redirect
    on its well-known path to AP backend (only for discovery
    from nickname though until Mastodon supports FEP-2c59)

This intentionally does not work for setups where FEP-2c59 is not
supported and the initially queried domain simply directly responds with
data containing a nickname from another domain’s authority without any
redirecty. (This includes the setup currently recommended by Mastodon,
when enriching an AP actor. Once Mastodon supports FEP-2c59 though its
setup will start to work again too automatically).
While technically possible to cross-verify the data with the nickname
domain, the existing validation logic is already complex enough and
such cross-validation needs extra safety measures to not get trapped
into infinite loops. Such setups are considered broken.
2026-03-12 00:00:00 +00:00
Oneric
69622b3321 Drop obsolete kludge for a specific, dead instance
It doesn’t make sense in general (many implementations use ids not nicks in ap_id)
and just wastes time by making additional, unnecessary, failing network requests.
This arguably should have never been committed.
2026-03-12 00:00:00 +00:00
Oneric
1e6332039f user/fetcher: also validate user data from Update
And fixup sloppy test data
2026-03-12 00:00:00 +00:00
Oneric
eb15e04d24 Split user fetching out of general ActivityPub module
The ActivityPub module is already overloaded and way too large.
Logic for fetching users and user information is isolated from
all other parts of the ActivityPub module, so let’s split it out.
2026-03-12 00:00:00 +00:00
Oneric
25461b75f7 webfinger: split remote queries and local data generation
They do not share any logic and the lack of separation makese it easy
to end up in the wrong section with ensuing confusion.
2026-03-12 00:00:00 +00:00
Oneric
4a35cbd23d fed/out: expose webfinger property in local actors (FEP-2c59)
It makes discovery and validation of the desired webfinger address
much easier. Future commits will actually use it for validation
and nick change discovery.
2026-03-12 00:00:00 +00:00
Oneric
1cdc247c63 Temporarily disable customised webfinger hosts
Proper validation of nicknames must consider both the domain
the nickname is associated with _and_ the actor to be assigned this
nickname consent to this.
Prior attempts at securing this wer emade in
a953b1d927 and
0d66237205 but they do not suffice.

The existing code attempted to validate webfinger responses independent
of the actual ActivityPub actor data and only ever consider the former
(yet failed to properly validate even this).

When resolving a user via a user-provided nickname the assignment
done by the provided URL was simply trusted regardless of the actors
AP host or data. When the nresolving the AP id, the nickname from this
original WebFinger query was passed along as pre-trusted data overriding
any discovery or validation from the actual actors side.
This allowed malicious actors to serve rogue WebFinger data associating
arbitrary actors with any nicknames they wished. Prompting servers to
resolve this rogue nickname then creates this nonconsensual assignment.

Notably, the existing code’s attempt at verification (only for domain
consent) used the originally requested URL for comparison against the
domain in the nickname handle. This effectively disabled custom
WebFinger domains for honest servers unless using an host-meta LRDD link.
(While LRDD links were recommend in the past by both *oma nad Mastodon,
 today most implementations other than *oma instead
 recommend setups emplyoing HTTP redirects.)
Still, this strictness did not prevent spoofing by malicious server.
It did however mean that rogue nickname assignments from an initial
nickname-based discovery were at least undone on the next user refresh
provided :pleroma, Pleroma.Web.WebFinger, :update_nickname_on_user_fetch
was not changed from its default truthy value.
(A renewed fetch via the rogue nickname would re-establish it though)

When enriching an already resolved ActivityPub actor to discover its
nickname the WebFinger query was not done with the unique AP id as a
resource, but a guessed nickname handle.
Furthermore, the received WebFinger response was not validated
to ensure the ActivityPub ID the WebFinger server pointed to
for the final nickname matched the actual ID in the considered
AP actor data.
While the faulty request URI check described above provides some
friction for malicious actors, it is still possible for mischiveous
AP instances to setup a rogue LRDD link poitning to a third-party
domain’s WebFInger and using the freedom provided by the LRDD link
to overwrite the resource value we provide in the lookup. Thus usurping
existing nicknames in another domains authority.

Proposed tweaks to the existing, faulty checks to work with
HTTP-redirect-based custom WebFInger domains would have made it
even easier to usurp nicknames from foreign domains.

For now simply disable custom WebFinger domains as a quick hotfix.
Subsequent commits will partially de-spaghettify the relevant code
and completely overhaul webfinger and nickname handling and validation.
2026-03-12 00:00:00 +00:00
Oneric
4c63243692 emoji/pack: fix in-place changes
Deleting a whole pack at once didn’t remove its emoji from memory
and ewly updated or added emoji used a wrong path. While the pack
also contains a path, this is the filesystem path while in-memory
Emoji need the URL path constructed from a constant prefix,
pack name and just the filename component of the filesystem file.
Test used to not check for this at all.

Fixes oversight in 318ee6ee17
2026-03-12 00:00:00 +00:00
a14d405e78 Update default config
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
2026-03-06 18:50:50 +01:00
f70eb0127e This shouldn't be an error
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
2026-03-03 00:22:04 +01:00
1721746ece Update test and config for Akkoma
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
2026-03-03 00:15:59 +01:00
Lain Soykaf
21e880d24e ConfigController: Don't allow whitelist modification. 2026-03-02 23:50:56 +01:00
Lain Soykaf
742de2bc9e Config: Don't crash on falsy whitelist config 2026-03-02 23:50:51 +01:00
Lain Soykaf
c446249f5a Cheatsheet: Fix double slash 2026-03-02 23:50:47 +01:00
Lain Soykaf
dc4f02f6f7 ConfigController: Don't allow updating the whitelist
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
2026-03-02 23:50:25 +01:00
Lain Soykaf
ac91a42f3e ConfigTest: Don't crash when whitelist is unset / disabled 2026-03-02 23:48:43 +01:00
9349c2cc3e Update cheatsheet
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
2026-03-02 23:48:16 +01:00
c379e3c8c0 Add task for filtering non-whitelisted configs
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
2026-03-02 23:48:11 +01:00
b3692e2e1c Add test for default whitelist config
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
2026-03-02 23:47:04 +01:00
8d4416fa70 Add sane defaults for :database_config_whitelist
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
2026-03-02 23:43:53 +01:00
e592337baf do not ever allow setting database_config_whitelist to database
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
2026-03-02 23:42:58 +01:00
Oneric
7b9a0e6d71 twitter_api/remote_follow: allow leading @ in nicknames
And never attempt to fetch nicknames as URLs
2026-03-02 00:00:00 +00:00
Oneric
5873e40484 grafana: update reference dashboard 2026-02-19 00:00:00 +00:00
Oneric
f8abae1f58 docs/admin/monitoring: document reference dashboard requires VM
As reported on IRC.
What exactly Prometheus takes offense with isn’t clear yet.
2026-02-19 00:00:00 +00:00
Oneric
4912e1782d docs/admin/monitoring: add instructions to setup outlier statistics 2026-02-19 00:00:00 +00:00
Oneric
6ed678dfa6 mix/uploads: fix rewrite_media_domain for user images
Fixes: AkkomaGang/akkoma#1064
2026-02-19 00:00:00 +00:00
7c0deab8c5 Merge pull request 'Fetcher: Only check SimplePolicy rules when policy is enabled' (#1044) from mkljczk/akkoma:fetcher-simple-policy into develop
Reviewed-on: AkkomaGang/akkoma#1044
Reviewed-by: Oneric <oneric@noreply.akkoma>
2026-02-18 13:37:27 +00:00
00fcffe5b9 fix test
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
2026-02-17 14:32:59 +01:00
246e864ce4 Merge pull request 'Mastodon-flavour (read) quotes API compat' (#1059) from Oneric/akkoma:masto-quotes-api into develop
Reviewed-on: AkkomaGang/akkoma#1059
2026-02-07 22:39:47 +00:00
c4bcfb70df Merge pull request 'Use local elixir-captcha clone' (#1060) from use-local-captcha-clone into develop
Reviewed-on: AkkomaGang/akkoma#1060
Reviewed-by: Oneric <oneric@noreply.akkoma>
2026-02-07 20:11:07 +00:00
cf8010a33e Merge pull request 'ensure utf-8 nicknames on nickname GETs and user validator' (#1057) from user-utf8 into develop
Reviewed-on: AkkomaGang/akkoma#1057
Reviewed-by: Oneric <oneric@noreply.akkoma>
2026-02-07 19:41:26 +00:00
4c657591a7 use version with git history 2026-02-07 19:40:09 +00:00
6ae0635da7 mix format 2026-02-07 19:28:13 +00:00
11dbfe75b9 pleroma git OBLITERATED 2026-02-07 19:16:32 +00:00
58ee25bfbb correct typings, duplicated check 2026-02-07 19:09:02 +00:00
Oneric
fd87664b9e api/statuses: allow quoting local statuses locally 2026-02-07 00:00:00 +00:00
Oneric
731863af9c api/statuses: allow quoting own private posts
Provided the quote is private too.

Ideally we’d inline the quoted, private status since not all
remotes may already know the old post and some implmentations
(ourselves included) have trouble fetching private posts.
In practice, at least we cannot yet make use of such an inlined post
anyway defeating the point. Implementing the inlining and ability to
make use of the inlined copy is thus deferred to a future patch.

Resolves: AkkomaGang/akkoma#952
2026-02-07 00:00:00 +00:00
Oneric
5b72099802 api/statuses: provide polyglot masto-and-*oma quote object
However, we cannot provide Masto-style shallow quotes this way.

Inspired-by: https://issues.iceshrimp.dev/issue/ISH-871#comment-019c24ed-c841-7de2-9c69-85e2951135ca
Resolves: AkkomaGang/akkoma#1009
2026-02-07 00:00:00 +00:00
Oneric
c67848d473 api/statuses: accept and prefer masto-flavour quoted_status_id
The quote creation interface still isn’t exactly drop-in compatible for
masto-only clients since we do not provide or otherwise deal with
quote-authorization objects which clients are encouraged to check before
even offering the possibility of attempting a quote. Still, having a
consistent paramter name will be easier on clients.

Also dropped unused quote_id parameter from ActivityDraft struct
2026-02-07 00:00:00 +00:00
Oneric
a454af32f5 view/nodeinfo: use string keys
This makes embedded nodeinfo data
consistent between local and remote users
2026-02-07 00:00:00 +00:00
Oneric
e557bbcd9d api/masto/account: filter embedded nodeinfo
The only kown user is akkoma-fe and it only ever
accesses the software information. For *oma instances
the full, unfiltered nodeinfo data can be quite large
adding unneeded bloat to API responses.
This would have become worse with the duplication of
account data needed for Masto quote post interop.

In case a client we’re not aware of actually uses more fields from
nodeinfo, a new but temporary config setting is provided as a workaround.

Fixes: AkkomaGang/akkoma#827
2026-02-07 00:00:00 +00:00
b20576da2e Merge pull request 'http: allow compressed responses, use system CA certs instead of CAStore fallback' (#1058) from Oneric/akkoma:http-lib-updates into develop
Reviewed-on: AkkomaGang/akkoma#1058
2026-01-30 20:14:53 +00:00
dee0e01af9 object/fetcher: only check SimplePolicy rules when policy is enabled
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
2026-01-30 00:00:00 +00:00
Oneric
e488cc0a42 http/adapter_helper: explicitly enable IPv4
Mint was upgraded in b1397e1670
2026-01-27 00:00:00 +00:00
Oneric
be21f914f4 mix: bump finch and use system cacerts
This upgrade pulls in a fix to better avoid killing re-actived pools,
obsoletes the need for our own HTTP2 server push workaround and allows
us to use system CA certs without breaking plain HTTP connections.

We tried to to the latter before on a per request basis, but this didn’t
actually do anything and we actually relied on the CAStore package
fallback the entire time. The broken attempt was removed in
ed5d609ba4.

Resolves: AkkomaGang/akkoma#880
2026-01-27 00:00:00 +00:00
Oneric
b9eeebdfd7 http: accept compressed responses
Resolves: AkkomaGang/akkoma#755
2026-01-27 00:00:00 +00:00
Oneric
c79e8fb086 mix: update Tesla to >= 1.16.0
This is the first release containg fixes making DecompressResponse
stable enough and suitable to be used by default allowing us to
profit from transport compression in obtained responses.

(Note: no compression is used in bodies we send out, i.e.
 ActivityPub documents federated to remote inboxes, since
 this will likely break signatures depending on whether
 the checksum is generated and checked before or after compression)

Ref: 5bc9b82823
Ref: 288699e8f5
2026-01-27 00:00:00 +00:00
8da6785c46 mix format 2026-01-25 01:31:26 +00:00
3deb267333 if we don't have a preferredUsername, accept standard fallback 2026-01-25 01:30:25 +00:00
0d7bbab384 ensure utf-8 nicknames on nickname gets and user validator 2026-01-25 01:29:10 +00:00
aafe0f8a81 Merge pull request 'scrubbers/default: Allow "mention hashtag" classes used by Mastodon' (#1056) from mkljczk/akkoma:allow-mention-hashtag into develop
Reviewed-on: AkkomaGang/akkoma#1056
2026-01-24 14:39:56 +00:00
24faec8de2 scrubbers/default: Allow "mention hashtag" classes used by Mastodon
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
2026-01-24 14:17:33 +01:00
816d2332ab Merge pull request 'Update docs/docs/administration/backup.md' (#1050) from patatas/akkoma:develop into develop
Reviewed-on: AkkomaGang/akkoma#1050
Reviewed-by: Oneric <oneric@noreply.akkoma>
2026-01-18 17:28:49 +00:00
a4a547e76e Update docs/docs/administration/backup.md
separate commands with semicolon (consistent with previous step in restore instructions)
2026-01-17 20:08:57 +00:00
Oneric
6cec7d39d6 db/migration/20251227000002: improve performance with older PostgreSQL
On fedi.absturztau.be the planner did not utilise the context
index for the original version leading to a query plan
100× worse than with this tweaked version.

With PostgreSQL 18 the relative difference is much smaller
but also in favour for the new version with the best observed
instance resulting in nearly half the estimated cost.
2026-01-13 00:00:00 +00:00
Oneric
3fbf7e03cf db/migration/20251227000000: add analyze statement
The second next migration greatly profits from this index
but sometimes the planner may not pick it up immediately
without an explicit ANALYZE call.
2026-01-12 00:00:00 +00:00
Oneric
31d277ae34 db: (re)add activity type index
Before 32a2a0e5fa the context index
acted as a (based on the name surprising) type index too.
Type-based queries are used in the daily pruning of old e.g. Delete
activities by PruneDatabaseWorker, when querying reports for the admin
API and inferring by the significant change in average execution time
a mysterious COUNT query we couldn’t associated with any code so far:

  SELECT count(a0."id") FROM "activities" AS a0 WHERE (a0."data"->>$2 = $1);

Having this as a separate index without pre-sorting results in
an overall smaller index size than merging this into the context index
again and based on it not having been sorted before non-context queries
appear to not significantly profit from presorting.
2026-01-11 00:00:00 +00:00
Oneric
3487e93128 api/v1/custom_emoji: improve performance
Metrics showed this endpoint taking unexpectedly long processing times
for a simple readout of an ETS table. Further profiling with
fprof revealed almost all time was spent in URI.merge.

Endpoint.url() is per its documented API contract guaranteed to have a
blank path and all our emoji file paths start with a slash.
Thus, a simple binary concat is equivalent to the result of URI.merge.

This cuts down the profiled time of just the fetching and
rendering of all emoji to a tenth for me. For the overall API request
with overhead for handling of the incoming response as well as encoding
and seding out of the data, the overall time as reported by phoenix
metrics dropped down by a factor of about 2.5.
With a higher count of installed emoji the overall relative time
reduction is assumed to get closer to the relative time reduction of
the actual processing in the controller + view alone.

For reference, this was measured with 4196 installed emoji:
(keep in mind enabling fprof slows down the overall execution)

          fprof'ed controller   Phoenix stop duration
 BEFORE:     (10 ± 0.3)s             ~250 ms
  AFTER:    (0.9 ± 0.06)s            ~100 ms

Note: akkoma-fe does not use this endpoint, but /api/v1/pleroma/emoji
defined in Pleroma.Web.TwitterAPI.UtilController:emoji/2 which only
emits absolute-path references and thus had no use for URI.merge anyway.
2026-01-11 00:00:00 +00:00
93b513d09c Merge pull request 'Fix conversations API' (#1039) from Oneric/akkoma:fix-conv-api into develop
Reviewed-on: AkkomaGang/akkoma#1039
2026-01-11 15:54:49 +00:00
Oneric
6443db213a conversation: remove unused users relationship
It is never used outside tests
and even there its correctness and/or
worth was questionable. The participations recipients
or just testing over the particiaptions’ user_id seem
like a better fit.
In particular this was always preloaded for API responses
needlessly slowing them down
2026-01-11 00:00:00 +00:00
Oneric
263c915d40 Create and bump conversations on new remote posts
The handling of remote and local posts should really be unified.
Currently new remote posts did not bump conversations
(notably though, until before the previous commit remote
 edits (and only edits), did in fact bump conversations due to being
 the sole caller of notify_and_stream outside its parent module)
2026-01-11 00:00:00 +00:00
Oneric
388d67f5b3 Don't mark conversations as unread on post edits
Without any indication of which post was edited this is only confusing and annoying.
2026-01-11 00:00:00 +00:00
Oneric
6adf0be349 notifications: always defer sending
And consistently treat muted notifications.

Before, when Notifications where immediately sent out, it correctly
skipped streaming nad web pushes for muted notifications, but the
distinction was lost when notification sending was deferred.
Furthermore, for users which are muted, but are still allowed to
create notifications, it previously (sometimes) automatically marked
them as read. Now notifications are either always silent, meaning
 - will not be streamed or create web pushes
 - will automatically be marked as read on creation
 - should not show up unless passing with_muted=true
or active, meaning
 - will be streamed to active websockets and cause web pushes if any
 - must be marked as read manually
 - show up without with_muted=true

Deferring sending out of notifications isdesirable to avoid duplicate
sending and ghost notifications when processing of the actiity fails
later in the pipeline and avoids stalling the ongoing db transaction.

Inspired by https://git.pleroma.social/pleroma/pleroma/-/merge_requests/4032
but the actual implementation differs to not break with_muted=true.
2026-01-11 00:00:00 +00:00
Oneric
2516206a31 conversation: include owner in recipients upon creation
Participation.set_Recipients already does something equivalent,
but it was forgotten here.
2026-01-11 00:00:00 +00:00
Oneric
9311d603fb conversation_controller: skip superfluous second order clause
This might also have prevented utilising the pre-sorted index.
2026-01-11 00:00:00 +00:00
Oneric
34df23e468 api/masto/conversation: fix pagination over filtered blocks
Previously the view received pre-filtered results and therefore
pagination headers were also only generated with pre-filtered
results in mind. Thus the next page would again traverse over
previously seen but filtered out entries.
Curucially, since this last_activity filtering is happening _after_
the SQL query, it can lead to fewer results being returned than
requested and in particular even zero results remaining.
Sufficiently large blocks of filtered participations thus caused
pagination to get stuck and abort early.

Ideally we would like for this filtering to occur as part of the SQL
query ensuring we will laways return as many results as are allowed as
long as there are more to be found. This would oboslete this issue.
However, for the reasons discussed in the previous commit’s message
this is currently not feasible.

The view is the only caller inside the actual application of the
for_user_with_pagiantion* functions, thus we can simply move filtering
inside the view allowing the full results set to be used when generating
pagination headers.
This still means there might be empty results pages, but now with
correct pagination headers one can follow to eventually get the full set
2026-01-11 00:00:00 +00:00
Oneric
1029aa97d2 api/masto/conversations: paginate by last status id
The old pagination logic was inconsistent and thus broken.
It used to order conversations based on updated_at but generated
pagination HTTP Link headers based on participation IDs.
Thus entries could repeat or be left out entirely.

Notably using updated_at also lead to bumps when
merely marking an individual conversation as read,
though not when marking _all_ conversations as read in bulk
since Repo.update_all does not touch date fields autoamtically.

For consistent and sensible "last active" ordering this is replaced
by using the flake ID (which contains the date) of the last status
which bumped the conversation for both ordering and pagination
parameters. This takes into account whether the status was
visible to the participation owner at the time of posting.

Notably however, it does not care about whether the status continues to
exist or continues to be visible to the owner. Thus this is not marked
as a foreign key and MUST NOT be blindly used as such!
For the most part, this should be considered as just a funny timestamp,
which is more resiliant against spurious bumps than updated_at, offers
deterministic sorting for simulateanously bumped conversations and
better usability in pagination HTTP headers and requests.

Implementing this as a proper foreign key with continuously enforced
visibility restrictions was considered. This would allow to just load
the "last activity" by joining on this foreign key, to immediately
delete participations once they become empty and obsoleting the
pre-existing post-query result filtering.
However, maintaining this such that visibility restrictions are
respected at all times is challenging and incurs overhead.
E.g. unfollows may retroactively change whether the participation owner
is allowed to access activities in the context.

This may be reconsidered in the future once we are clearer
on how we want to (not) extend conversation features.

This also improves on query performance by now using a multi-row,
pre-sorted index such that one user querying their latest conversations
(a frequent operation) only needs to perform an index lookup (and
loading full details from the heap).
Before queries from rarely active users needed to traverse newer
conversations of all other users to collect results.
2026-01-11 00:00:00 +00:00
Oneric
ebd22c07d1 test/factory: take override paramters at more factories 2026-01-11 00:00:00 +00:00
Oneric
97b2dffcb9 pagination: allow custom pagination ids
While we already use wrapped return lists for
HTML pagination headers, currently SQL queries
from the SQL pagination helper use the primary key "id"
of some given table binding. However, for participations
we would like to be able to sort by a field which is not
a primary key. Thus allow custom field names.
2026-01-11 00:00:00 +00:00
Oneric
613135a402 ap: fix streamer crashes on new, locally initiated DM threads
Since side effect handling of local notes currently can only immediately
stream out changes and notifications (eventhough it really shouldn’t for
many more reasons then this) the transaction inserting the various
created objects into the database is not finished yet when StreamerView
tries to render the conversation.

This would be fine were it using the same db connection as the
inserting transaction. However, the streamer is a different process
and gets sent the in-memory version of the participation.

In the case of newly created threads the preloaded the streamer process
will not be able to load preload it itself since it uses a different db
connection and thus cannot see any effects of the unfinished transaction
yet. Thus it must be preloaded before passing it to Streamer.

Notably, the same reasoning applies to the new status activity itself.
Though fetching the activity takes more time with several prepatory
queries and in practice it appears rare for the actual activity query to
occur before the transaction finished.
Nonetheless, this too should and will be fixed in a future commit.

Fixes: AkkomaGang/akkoma#887
2026-01-11 00:00:00 +00:00
Oneric
120a86953e conversation: don't create participations for remote users
They can never query the participation via API nor edit the recipients,
thus such particiaptions would just forever remain unused and are
a waste of space.
2026-01-11 00:00:00 +00:00
Oneric
8d0bf2d2de conversation/participation: fix restrict_recipients
For unfathomable reasons, this did not actually use recipiepent (ship)
information in any way, but compared against all users owning a
participation within the same context.
This is obviously contradicting *oma’s manually managed recipient data
and even without this extension breaks if there are multiple distinct
subthreads under the same (possibly public) root post.
2026-01-11 00:00:00 +00:00
Oneric
32ec7a3446 cosmetic/conversation/participation: mark eligible functions as private 2026-01-11 00:00:00 +00:00
Oneric
9fffc49aaa conversation/participation: delete unused function
Redundant with unread_count/1 and only the latter is actually used
2026-01-11 00:00:00 +00:00
Oneric
32a2a0e5fa db: tweak activity context index
Presorting the index will speed up all context queries
and avoids having to load the full context before sorting
and limiting can even start.
This is relevant when get the context of a status
and for dm conversation timelines.

Including the id column adds to the index size,
but dropping non-Creates also brings it down again
for an overall only moderate size increase.
The speedup when querying large threads/conversations
is worth it either way.

Otherwise, the context attribute is only used as a condition in
queries related to notifications, but only to filter an otherwise
pre-selected set of notifications and this index will not be relevant.
2026-01-11 00:00:00 +00:00
Oneric
5608f974a3 api: support with_muted in pleroma/conversation/:id/statuses
It does not make sense to check for thread mutes here though.
Even if this thread was muted, it being explicitly fetched
indicates it is desired to be displayed.
While this used to load thread mutes, it didn’t actually apply them
until now, so in regard it does not change behaviour either and we
can optimise the query by not loading thread mutes at all.

This does change behavior of conversation timelines however
by now omitting posts of muted users by default, aligning it
with all other timelines.
2026-01-11 00:00:00 +00:00
Oneric
f280dfa26f docs/monitoring: note reference dashboard testing 2026-01-11 00:00:00 +00:00
Oneric
0326330d66 telemetry: log which activities failed to be delivered 2026-01-11 00:00:00 +00:00
d35705912f Merge pull request 'webfinger: accept canoncial AP type in XML and don’t serve response for remote users' (#1045) from Oneric/akkoma:fix-webfinger-type into develop
Reviewed-on: AkkomaGang/akkoma#1045
2026-01-10 20:23:53 +00:00
Oneric
74fa8f5581 webfinger: don’t serve response for remote users’ AP id 2026-01-10 00:00:00 +00:00
Oneric
967e2d0e71 webfinger: mark represent_user as private 2026-01-10 00:00:00 +00:00
Oneric
ee7e6d87f2 fed/in: accept canoncial AP type in XML webfinger data
This was supposed to be already handled for both XML and JSON
with d1f6ecf607 though the code
failed to consider variable scopes and thus was broken and
actually just a noop for XML.

For inexplicable reasons 1a250d65af
just outright removed both the failed attempt to parse the canonical
type in XML documents and also serving of the canonical type in our own
XML (and only XML) response.

With the refactor in 28f7f4c6de
the canoncial type was again served in both our own JSON and XML
responses but parsing of the canonical type remained missing
from XML until now.
2026-01-10 00:00:00 +00:00
e326285085 Merge pull request 'Various fixes' (#1043) from Oneric/akkoma:varfixes into develop
Reviewed-on: AkkomaGang/akkoma#1043
2026-01-05 14:38:31 +00:00
Oneric
80817ac65e fed/out: also represent emoji as anonymous objects in reactions
It did not use the same Emoji object template as other occurrences.
This also fixes an issue with the icon URL not being properly encoded
as well as an inconsistency regarding the domain part of remote
reactions in retractions. All places use the image URL domain
except the query to find the activity to retract relie on the id.
Even before this change this made it impossible to retract remote
emoji reactions if the remote either doesn't send emoji IDs or
doesn't store images on the ActivityPub domain.

Addresses omission in 4ff5293093

Fixes: AkkomaGang/akkoma#1042
2026-01-05 00:00:00 +00:00
Oneric
5f4083888d api/stream: don’t leak hidden follow* counts in relationship updates
Based on the Pleroma patch linked below, but not needlessly
hiding the count if only the listing of the specific follow* accounts is
hidden while counts are still public.
https://git.pleroma.social/pleroma/pleroma/-/merge_requests/4205

Co-authored-by: nicole mikołajczyk <git@mkljczk.pl>
2026-01-05 00:00:00 +00:00
Oneric
eb08a3fff2 api/pleroma/conversation: accept JSON body to update conversation 2026-01-05 00:00:00 +00:00
Oneric
d6209837b3 api/v1/filters: escape regex sequence in user-provided phrases 2026-01-05 00:00:00 +00:00
Oneric
59b524741d web/activity_pub: drop duplicated restrict_filtered 2026-01-05 00:00:00 +00:00
e941f8c7c1 Merge pull request 'Support Mastodon-compatible translations API' (#1024) from mkljczk/akkoma:akkoma-mastoapi-translations into develop
Reviewed-on: AkkomaGang/akkoma#1024
2026-01-04 16:11:27 +00:00
b147d2b19d Add /api/v1/instance/translation_languages
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
2026-01-04 00:00:00 +00:00
d65758d8f7 Deduplicate translations code
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
2026-01-04 00:00:00 +00:00
f5ed0e2e66 Inline translation provider names in function
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
2026-01-04 00:00:00 +00:00
3b74ab8623 Support Mastodon-compatible translations API
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
2026-01-04 00:00:00 +00:00
c971f297a5 Merge pull request 'Deal with elixir 1.19 warnings and test failures' (#1029) from Oneric/akkoma:elixir-1.19-warnings into develop
Reviewed-on: AkkomaGang/akkoma#1029
2025-12-29 00:09:37 +00:00
720b51d08e Merge pull request 'Update ci build scripts for 1.19' (#1038) from ci-builds-otp28 into develop
Reviewed-on: AkkomaGang/akkoma#1038
2025-12-28 21:57:15 +00:00
27b725e382 Update ci/build-all.sh 2025-12-28 21:52:01 +00:00
Oneric
86d62173ff test: fix regex compare for OTP28
This was technically already incorrect before and pointed out as such in
documentation, but in practice worked well until OTP28’s regex changes.
2025-12-28 00:00:00 +00:00
Oneric
cbae0760d0 ci: adjust elixir and OTP versions 2025-12-28 00:00:00 +00:00
Oneric
1fed47d0e0 user/signing_key: fix public_key functions and test 2025-12-25 00:00:00 +00:00
Oneric
712a629d84 Fix test in- and exclusion
We had a feww files matching neither inclusion nor exclusion rules.
With elixir 1.19 this creates a warning.
Most were intended to be ignored and thus we now override the default
rules in mix.exs to be explicit about this. However, two test files were
simply misnamed and intended to run, but untill now skipped.

Notably, the signing key test for shared key ids currently fails due to
a missing mock. For now this faulty test is simply disabled, the next
commit will fix it
2025-12-25 00:00:00 +00:00
Oneric
84ad11452e test: fix elixir 1.19 warnings in tests
Except for the struct comparisons, which was a real bug,
the changes are (in current versions) just cosmetic
2025-12-25 00:00:00 +00:00
Oneric
ae17ad49ff utils: comply with elixir 1.19 soft-requirement for parallel compiles
Elixir 1.19 now requires (with a deprecation warning) return_diagnostics
to be set to true for parallel compiles. However, this parameter was only
added in Elixir 1.15, so this raises our base requirement.
Since Elixir 1.14 is EOL anyway now this shoulöd be fine though.
2025-12-25 00:00:00 +00:00
Oneric
e2f9315c07 cosmetic: adjust for elixir 1.19 struct update requirments
When feasible actually enforce the to-be-updated data being the desired struct type.
For ActivityDraft this would add too much clutter and isn't necessary
since all affected functions are private functions we can ensure only
get correct data, thus just use simple map-update syntax here.
2025-12-25 00:00:00 +00:00
Oneric
fdd6bb5f1a mix: define preferred env in cli()
Defining inside project() is deprecated
2025-12-25 00:00:00 +00:00
Oneric
7936c01316 test/config/deprecations: fix warning comparsion for elixir 1.19
It can include a terminal-colour sequence before the final newline
2025-12-25 00:00:00 +00:00
Oneric
d92f246c56 web/ap/object_validators/tag: fix hashtag name normalisation
When federating out, we add the leading hashtag back in though
2025-12-25 00:00:00 +00:00
Oneric
8f166ed705 cosmetic: adjust for elixir 1.19 mix format 2025-12-25 00:00:00 +00:00
Oneric
b44292650e web/telemetry: fix HTTP error mapping for Prometheus
Fixes omission in commit 2b4b68eba7
which introduced a more concise response format for HTTP errors.
2025-12-24 00:00:00 +00:00
68c79595fd Merge pull request 'Fix more interactions with invisible posts and corresponding data leaks' (#1036) from Oneric/akkoma:fix-interacting-nonvisible-posts into develop
Reviewed-on: AkkomaGang/akkoma#1036
2025-12-24 02:43:00 +00:00
Oneric
be7ce02295 test/mastodon_api/status: insert mute before testing unmute
Currently the test is still correctly sensitive to the visibility issue
it wants to test, but it easy to see how this could chane in the future
if it starts considering whether a mute existed in the first place.
Inserting a mute first ensures the test will keep working as intended.
2025-12-24 00:00:00 +00:00
Oneric
b50028cf73 changelog: add entries for recent fixes 2025-12-24 00:00:00 +00:00
Oneric
82dd0b290a api/statuses/unfav: do not leak post when acces to post was lost
If a user successfully favourited a post in the past (implying they once
had access), but now no longer are alllowed to see  the (potentially
since edited) post, the request would still process and leak the current
status data in the response.

As a compromise to still allow retracting past favourites (if IDs are
still cached), the unfavouriting operation will still be processed, but
at the end lie to the user and return a "not found" error instead of
a success with forbidden data.

This was originally found by Phantasm and fixed in Pleroma as part of
https://git.pleroma.social/pleroma/pleroma/-/merge_requests/4400
but by completely preventing the favourite retraction.
2025-12-24 00:00:00 +00:00
Oneric
981997a621 api/statuses/bookmark: improve response for hidden or bogus targets
Also fixes Bookmark.destroy crashing when called with
parameters not mapping to any existing bookmark.

Partially-based-on: fe7108cbc2
Co-authored-by: Phantasm <phantasm@centrum.cz>
2025-12-24 00:00:00 +00:00
Lain Soykaf
126ac6e0e7 Transmogrifier: Handle user updates.
Cherry-picked-from: 98f300c5ae
2025-12-24 00:00:00 +00:00
Lain Soykaf
3e3baa089b TransmogrifierTest: Add failing test for Update.
Cherry-picked-from: ed538603fb
2025-12-24 00:00:00 +00:00
Oneric
25d27edddb ap/transmogrifier: ensure attempts to update non-updateable data are logged
Often raised errors get logged automatically,
but not always and here it doesn't seem to happen.
I’m not sure what the criteria for it being logged or not are tbh.
2025-12-24 00:00:00 +00:00
300744b577 CommonAPI: Forbid disallowed status (un)muting and unpinning
When a user tried to unpin a status not belonging to them, a full
MastoAPI response was sent back even if status was not visible to them.

Ditto with (un)mutting except ownership.

Cherry-picked-from: 2b76243ec8
2025-12-24 00:00:00 +00:00
ac94214ee6 Transmogrifier: update internal fields list according to constant
Adjusted original patch to drop fields not present in Akkoma

Cherry-picked-from: 3f16965178
2025-12-24 00:00:00 +00:00
Oneric
a2d156aa22 cosmetic/common_api: simplify check_statuses_visibility 2025-12-24 00:00:00 +00:00
31d5f556f0 CommonAPI: Fail when user sends report with posts not visible to them
Cherry-picked-from: 2b76243ec8
2025-12-24 00:00:00 +00:00
83832f4f53 bump version, update changelog 2025-12-07 05:02:26 +00:00
Oneric
3175b5aa25 docs: update for recent deprecations and removals 2025-12-05 00:00:00 +00:00
Oneric
6c1afa42d2 changelog: mention (repeated) deprecation of dm timeline
As discussed on IRC this is already buggy on large instnaces
and conflicts with future cleanup and optimisation work.

The only remaining users currently appear to be Pleroma-derived web
frontends, namely akkoma-fe, pleroma-fe, pl-fe and Mangane.
We will switch akkoma-fe over to conversations before the actual
removal.
2025-11-28 18:37:59 +01:00
3a04241e3b Merge pull request 'include pl-fe in available frontends' (#1023) from mkljczk/akkoma:pl-fe into develop
Reviewed-on: AkkomaGang/akkoma#1023
2025-11-28 17:04:52 +00:00
353c23c6cd include pl-fe in available frontends
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
2025-11-28 09:22:31 +01:00
70877306af Merge pull request 'RFC: handling of third-party frontends in default available FE list' (#945) from Oneric/akkoma:third-party-frontends into develop
Reviewed-on: AkkomaGang/akkoma#945
2025-11-27 21:32:02 +00:00
9abce8876a Merge pull request 'Add mix task to rewrite media domains' (#961) from rewrite-media-urls into develop
Reviewed-on: AkkomaGang/akkoma#961
Reviewed-by: Oneric <oneric@noreply.akkoma>
2025-11-27 20:41:49 +00:00
22d1b08456 Merge pull request 'Tweak users database indexes and drop exclude_visibilities' (#1019) from Oneric/akkoma:db-index-tweaks into develop
Reviewed-on: AkkomaGang/akkoma#1019
2025-11-27 19:43:23 +00:00
Oneric
453ab11fb2 changelog: add missing entries 2025-11-27 00:00:00 +00:00
Oneric
0aeeaeb973 api: tentatively remove exclude_visibility parameter
This was originally added in a97b642289 as
part of https://git.pleroma.social/pleroma/pleroma/-/merge_requests/1818
with the hope/intent of using it to hide DMs by default from timelines.

This never came to be with thisd parameter still being unused today in
al of akkoma-fe, pleroma-fe, Husky, pl-fe, Mangange, fedibird-fe and
masto-fe. Given thypically only a small percentage of posts are DMs,
as well as issues with the SQL function and index used to implement this
a removal likely won't bother anyone while allowing us to clean up
bugged code and improve actually used functions.

While unlikely, we don't have a way to ascertain for sure whether there
are still users so for now only drop it from API Spec which leads to
the parameter being stripped out before processing and keep the actual
logic. If any users exist and complain we can easily revert this, if not
we will follow up with a proper cleanup.
2025-11-27 00:00:00 +00:00
Oneric
aac6086ca6 db: convert indexes to partial ones where appropriate
This reduces both the size the indexes take on the disk
and the overhead of maintaining it since it now only
needs to be updated for some entries.

For the email index restriction queries needed to be updated,
the last_active_at index is used only by one query which already
also restricted by "local" and for the remaining restrictions
no query adaptations are needed.
2025-11-27 00:00:00 +00:00
078c73ee2c Merge pull request 'Redirect /users/:nickname.rss to /users/:nickname/feed.rss instead of .atom' (#1022) from mkljczk/akkoma:akkoma-feed-redirect into develop
Reviewed-on: AkkomaGang/akkoma#1022
Reviewed-by: Oneric <oneric@noreply.akkoma>
2025-11-26 20:56:12 +00:00
8fd8e7153e Redirect /users/:nickname.rss to /users/:nickname/feed.rss instead of .atom
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
2025-11-26 16:55:50 +01:00
90e18796cf Merge pull request 'Hide private keys and password hashes from logs by default' (#1021) from Oneric/akkoma:log-sanitisation into develop
Reviewed-on: AkkomaGang/akkoma#1021
2025-11-25 21:13:50 +00:00
Oneric
6ad9f87a93 deps: upgrade http_signatures
The only change is hiding key material from logs
2025-11-25 00:00:00 +00:00
Oneric
6d241a18da Hide the most sensitive user data from logs
Else admins may accidentally leak password (hashes)
or expose their users emails when sharing logs for debugging
2025-11-25 00:00:00 +00:00
Oneric
d81b8a9e14 http/middleware/httpsig: drop opt-in privkey scrubbing
This was replaced by the always-on exclusion from the inspect protocol
implemented in the previous commit which is far more robus.

This partially reverts commit 2b4b68eba7;
the more detailed response for generic HTTP errors is retained.
2025-11-25 00:00:00 +00:00
Oneric
5d8cdd6416 Always hide key material of user signing keys
This is far more robst than trying to manually scrub this in all affected paths
2025-11-25 00:00:00 +00:00
f5d78b374c Merge pull request 'Fix ActivityPub fetch sanitisation' (#1018) from Oneric/akkoma:fix-fetch-serve-sanitisation into develop
Reviewed-on: AkkomaGang/akkoma#1018
2025-11-24 00:35:24 +00:00
Oneric
272799da62 test: add more representation tests for perpare_outgoing
In particular this covers the case
e88f36f72b was meant to fix and
the case from AkkomaGang/akkoma#1017
2025-11-24 00:00:00 +00:00
Oneric
85171750f1 fed/fetch: use same sanitisation logic as when delivering to inboxes
Duped code just means double the chance to mess up. This would have
prevented the leak of confidential info more minimally fixed in
6a8b8a14999f3ed82fdaedf6a53f9a391280df2f and  now furthermore
fixes the representation of Update activites which _need_ to have their
object inlined, as well as better interop for follow Accept and Reject
activities and all other special cases already handled in Transmogrifier.
It also means we get more thorough tests for free.

This also already adds JSON-LD context and does not add bogus Note-only
fields as happened before due to this views misuse of prepare_object
for activities. The doc of prepare_object clearly states it is only
intended for creatable objects, i.e. (for us) Notes and Questions.
2025-11-24 00:00:00 +00:00
Oneric
eaad13e422 fed/out: ensure we never serve Updates for objects we deem static 2025-11-24 00:00:00 +00:00
Oneric
6ef13aef47 Add voters key to internal object fields
It is inlined and used to keep track of who already voted for a poll.
This is expected to be confidential information and must no be exposed
2025-11-24 00:00:00 +00:00
Oneric
ef029d23db fed/fetch: don't serve unsanitised object data for some activities
When the object associated with the activity was preloaded
(which happens automatically with Activity.normalize used in the
 controller) Object.normalize’s "id_only" option did not actually work.
This option and it’s usage were introduced to fix display of Undo
activities in e88f36f72b.
For "Undo"s (and "Delete"s) there is no object preloaded
(since it is already gone from the database) thus this appeared
to work and for the particular case considered there in fact did.
Create activities use different rendering logic and thus remained
unaffected too.

However, for all other types of Activities (yes, including Update
which really _should_ include a properly sanitised, full object)
this new attempt at including "just the id", lead to it instead
including the full, unsanitised data of the referenced object.

This is obviously bad and can get worse due to access restrictions
on the activity being solely performed based on the addressing
of the activity itself, not of the (unintentionally) embedded
object.

Starting with the obvious, this leaks all "internal" fields
but as already mentioned in 8243fc0ef4
all current "internal" fields from Constants.object_internal_fields
are already publicised via MastoAPI etc anyway. Assuming matching
addressing of the referenced object and activity this isn't problematic
with regard to confidentiality.
Except, the internal "voters" field recording who voted for a poll
is currently just omitted from Constants.object_internal_fields
and indeed confidential information (fix in subsequent commit).
Fortunately this list is for the poll as a whole and there are no
inlined lists for individual choices. While this thus leaks _who_
voted for a poll, it at least doesn't directly expose _what_ each voter
chose if there are multiple voters.

As alluded to before, the access restriction not being aware
of the misplaced object data into account makes the issue worse.
If the activity addressing is not a subset of the referenced object’s
addressing, this will leak private objects to unauthorised users.
This begs the question whether such mismatched addressing can occur.
For remote activities the answer is ofc a resounding YES,
but we only serve local ActivityPub objects and for the latter
it currently(!) seems like a "no".
For all intended interactions, the user interacting must already have
access to the object of interest and our ActivityPub Builder
already uses a subset of the original posts addressing for
posts not publicly accessible. This addressing creation logic
was last touched six years ago predating the introduction of this
exposure blunder.
The rather big caveat her being, until it was fixed just yesterday in
dff532ac72 it was indeed possible to
interact with posts one is not allowed to actually see. Combined, this
allowed unauthorised access to private posts. (The API ID of such
private posts can be obtained e.g. from replies one _is_ allowed to see)

During the time when ActivityPub C2S was supported there might have been
more ways to create activities with mismatched addressing and sneak a
peek on private posts. (The AP id can be obtained in an analogous way)

Replaces and fixes e88f36f72b.
Since there never were any users of the
bugged "id_only" option it is removed.

This was reported by silverpill <silverpill@firemail.cc> as an
ActivityPub interop issue, since this blunder of course also
leads to invalid AP documents by adding an additional layer
in form of the "data" key and directly exposing the internal
Pleroma representation which is not always identical to valid AP.

Fixes: AkkomaGang/akkoma#1017
2025-11-24 00:00:00 +00:00
8fd2fb995f Merge pull request 'test: raise default assert_receive timeout' (#1016) from Oneric/akkoma:tmp into develop
Reviewed-on: AkkomaGang/akkoma#1016
2025-11-23 15:34:02 +00:00
a5683966a8 Merge pull request 'Add banner.png back' (#1015) from mkljczk/akkoma:banner-png into develop
Reviewed-on: AkkomaGang/akkoma#1015
Reviewed-by: Oneric <oneric@noreply.akkoma>
2025-11-23 12:18:21 +00:00
Henry Jameson
798beec97b config: add pleroma’s pleroma-fe to frontends available by default 2025-11-23 00:00:00 +00:00
Oneric
e6ce7751a9 test: raise default assert_receive timeout
In our CI we sometimes fail assert_receive statmenets even in retry
making CI results unreliabl'ish. The default is 100ms, now it is 1s.

See e.g: https://ci.akkoma.dev/repos/1/pipeline/504/5
2025-11-23 00:00:00 +00:00
0b049c3621 Add banner.png back
It was mistakenly removed as part of the bundled frontend in
82fa766ed7 but actually it
is used as the default value for user profile banners.

Mastodon API typespecs define the relevant field as non-nullable in
585545d0d5/app/javascript/mastodon/api_types/accounts.ts (L30)
and clients do in fact rely on it being non-null, meaning we can't just
omit the value if a user didn't setup a bespoke banner.

Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
2025-11-23 00:00:00 +00:00
Oneric
7d480c67d1 frontend: print warning for third-party frontends
For future use. Mark all current frontends as first-party.
The key is named 'blind_trust' instead of 'first_party' to (hopefully)
increase the chances someone notices if some random, malicous frontend
adds the key into their install instructions
2025-11-23 00:00:00 +00:00
Oneric
8352c1f49d frontend: mark functions without external users as private 2025-11-23 00:00:00 +00:00
c7d75ca0d3 Merge pull request 'api: ensure only visible posts are interactable' (#1014) from Oneric/akkoma:can-you-see-it-too into develop
Reviewed-on: AkkomaGang/akkoma#1014
2025-11-22 22:47:12 +00:00
e3dd94813c Merge pull request 'Fix mentioning complex usernames' (#1012) from Oneric/akkoma:fix-nonalphanum-mentions into develop
Reviewed-on: AkkomaGang/akkoma#1012
2025-11-22 21:21:51 +00:00
bc68761b1d add doc, change IO to shell_info 2025-11-22 17:42:42 +00:00
c144bc118d Merge branch 'develop' into rewrite-media-urls 2025-11-22 17:31:08 +00:00
b9e70c29ef Merge pull request 'Adjust rss/atom PR' (#1007) from akkoma-hashtag-feeds-restricted into develop
Reviewed-on: AkkomaGang/akkoma#1007
2025-11-22 17:24:03 +00:00
Oneric
dff532ac72 api: ensure only visible posts are interactable
It doesn't make sense to like, react, reply, etc to something you cannot
see and is unexpected for the author of the interacted with post and
might make them believe the reacting user actually _can_ see the post.

Wrt to fav, reblog, reaction indexes the missing visibility check was
also leaking some (presumably/hopefully) low-severity data.

Add full-API test for all modes of interactions with private posts.
2025-11-22 00:00:00 +00:00
Oneric
810e3b1201 Fix mentioning complex usernames
The updated linkify is much more liberal about usernames in mentions.

Fixes: AkkomaGang/akkoma#1011
2025-11-19 00:00:00 +00:00
5e4475d61e Merge pull request 'Purge broken, unused and/or useless features' (#1008) from Oneric/akkoma:purge-broken into develop
Reviewed-on: AkkomaGang/akkoma#1008
2025-11-18 22:08:04 +00:00
d96c6f438d Merge pull request 'Fix generic type and alt text for incoming federated attachments' (#1010) from Oneric/akkoma:fedin-attachment-fixes into develop
Reviewed-on: AkkomaGang/akkoma#1010
2025-11-17 16:29:42 +00:00
Oneric
9d19dbab99 app: probe if any users of thread containment exist
If "thread containment" isn’t skipped the API will only return posts
whose entire ancestor chain is also visible to the current user.
For example if this contianment is active and the current user
follows A but not B and A replies to a followers-only post of B
the current user will not be able to see A’s post eventhough
per ActivityPub-semantics (an behaviour of other implementations)
the current user was addressed and has read permissions to A’s post.
(Though this semantics frequently surprise some users.)

There is a user-level option to control whether or not to perform
this kind of filtering and a server-wide config option under :instance.
If this containment is _disabled_ (i.e. skip_thread_containment: true)
server-wide, user-level options are ignored an filtering never takes
place.

This is cheked via the database function "thread_visibility" which
recursively calls itself on the ancestor of the currently inspected post
and for each post performs additional queries to check the follow
relationship between author and current user.
While this implementation introduced in
https://git.pleroma.social/pleroma/pleroma/-/merge_requests/971
performs better than the previous elixir-side iteration due to
less elixir-database roundtrips the perf impact is still ridiculously
large and when fetching and entire conversation / context at once there
are many redundnat checks and queries.
Due to this an option to dis/enable the "containment" was added and
promptly defaulted to disabling "containment" in
593b8b1e6a six years ago.
This default remained unchanged since and the implementation wasn’t
overhauled for improved performance either. Essentially this means
the feature has already been entirely disabled oout-of-the box
without too much discoverability for the last six years. It is thus
not too unlikely that no actual users of it exist today.

The user-level setting also didn’t made its way into any known clients.
Surveying current versions of akkoma-fe, husky, pleroma-fe, pl-fe,
Mangane and just to be sure also admin-fe, fedibird-fe and masto-fe
none of them appears to expose a control for the user-level setting.
pl-fe’s pl-api acknowledges the existance of the setting in the API
definition but the parameter appears unused in any actual logic.
Similarly Mangane and pl-fe have a few matches for
"skip_thread_visibility" in test samples of user setting responses
but again no actual uses in active code.

While the idea behind the feature is sensible (though care must be taken
to not mislead users into believing _all_ software would apply the same
restrictions!), the current implementation is very much not sensible.
With the added code complexity and apparent lack of users it is unclear
whether keeping the feature around and attempting to overhaul the
implementation is even worth it.
Thus start pritning a big fat warning for any potentially existing users
prompting for feedback. If there are no responses two releases from now
on it will presumably be safe to just entirely drop it.
2025-11-17 00:00:00 +00:00
Oneric
ffeb70f787 Drop counter_cache stubbing out /api/v1/pleroma/admin/stats
It only served for a niche, admin nice-to-have informational stat
without too much value but was unreasonably costly to maintain
adding overhead with multiple queries added to all modifications
to the fairly busy activities table.

The database schema of the counter table and the activity_visibility function
used for counter updates also did not know about "local" visibility (nor the
recently removed "list" visibility) and misattributed them to the "direct" counter.

On my small instance this nearly halved the average
insert time for activiteis from 0.926 ms to 0.465 ms.
2025-11-17 00:00:00 +00:00
Oneric
865cfabf88 Drop conversation addressing
No known client ever used this. Currently among akkoma-fe, pleroma-fe,
Husky, Mangane and pl-fe only the latter acknowledes the existence of
the in_reply_to_conversation_id paramter in its API definitions,
but even pl-fe does never actually use the parameter anywhere.

Since the API parameter already was converted to DMs internally,
we do not need to make special considerations for already existing
old conversation-addressed posts. Since they share the context they
should also continue to show up in the intended thread anyway.

The pleroma.participants subkey mentioned in docs did already not exist
prior to this commit. Instead the accounts key doesn’t automatically update
and this affects conversations retrieved from the Mastodon API endpoint too
(which may be considered a pre-existing bug).

If desired clients can already avoid unintended participant additions
by using the explicit-addressing feature originally introduced in
https://git.pleroma.social/pleroma/pleroma/-/merge_requests/1239.
With the above-mentioned feature/bug of conversation participants
not updating automatically it can replace almost everything
conversation addressing was able to do. The sole exception being
creating new non-reply posts in the same context.
Neither conversation addressing nor explicit addressing
achieves robust, federated group chats though.

Resolves: AkkomaGang/akkoma#812
2025-11-17 00:00:00 +00:00
Oneric
271110c1a5 Drop broken list addressing feature
This feature was both conceptually broken and through bitrotting
the implementation was also buggy with the handling of certain
list-post interactions just crashing.

Remote servers had no way to know who belongs to a list and thus
posts basically showed just up as weird DM threads with different
participants on each instance. And while on the local instance
addition and removal from a listed grated and revoked post
access retroactively, it never acted retroactively on remotes.

Notably our "activity_visibility" database function also didn’t
know about "list visibility" instead treating them as direct messages.

Furthermore no known client actualy allows creating such messages
and the lack of complaints about the accumulutaed bugs supports
the absence of any users.

Given this there seems no point in fixing the implementation.
To reduce complexity of visibility handling it will be dropped instead.
Note, a similar effect with less federation weirdness can already be achieved
client-side using the explicit-addressing feature originally introduced in
https://git.pleroma.social/pleroma/pleroma/-/merge_requests/1239.

Ref: AkkomaGang/akkoma#812
2025-11-17 00:00:00 +00:00
Oneric
180a6ba962 api/views/status: prefer 'summary' for attachment alt texts
GtS started exclusively using it and it already worked with Mastodon.
See: https://codeberg.org/superseriousbusiness/gotosocial/issues/4524

Since we used to (implicitly) strip the summary field
this will not take effect retroactively.
2025-11-16 00:00:00 +00:00
Oneric
9bbebab1a2 api/views/status: fallback to generic type when deducing attachment type
While *oma, *key, GtS and even Mastodon federate a full media type for attachments,
posts from Bridgy only contain a generic type and the URLs also appear to never end
with a file exstension. This lead to our old type detection always classifying them
as "unknown" and it showing up like a generic document attachment in frontends.
We can (for vanilla Masto API clients) avoid this by falling back to the
federated generic type.
(Note: all other software mentioned at the start appears to always use "Document"
for the generic type of attachments regardless of the also federated actual full type)

For clients relying on the full mime type provided by an *oma extension,
like currently akkoma-fe, this in itself does not fix the display
but it is a necessary prerequisite to handling this more gracefully.
2025-11-16 00:00:00 +00:00
Oneric
33dbca5b3a api/views/status: fallback to binary MIME type instead of invalid 'image' type
A MIME type MUST always contain both the type and subtype part.
Also, we already add this binary type for new incoming attachments
without a pre-existing MIME type entry anyway.
2025-11-16 00:00:00 +00:00
c2e9af76a5 Merge branch 'develop' into akkoma-hashtag-feeds-restricted 2025-11-13 11:35:16 +00:00
703db7eaef use verified paths 2025-11-13 11:34:18 +00:00
0140643761 Merge pull request 'reverse_proxy: don't rely on header for body size' (#989) from Oneric/akkoma:revproxy-content-size into develop
Reviewed-on: AkkomaGang/akkoma#989
2025-11-13 10:44:25 +00:00
d4a86697d9 Merge pull request 'upgrade all deps' (#1002) from Oneric/akkoma:dep-update into develop
Reviewed-on: AkkomaGang/akkoma#1002
2025-11-09 14:38:07 +00:00
Oneric
6b0d4296d9 ci/publish: actually select arm64 releaser image 2025-11-09 00:00:00 +00:00
Oneric
6f971f10cf test/meilisearch: maybe fix flakyness
CI sometimes failed due to Mock already being started,
though I couldn’t reproduce it locally. Using non-global
mocks hopefully avoids this
2025-11-09 00:00:00 +00:00
Oneric
1bff36b990 ci/publish: fix syntax 2025-11-09 00:00:00 +00:00
Oneric
7a5c28a12a mix/deps: upgrade phoenix_ecto 2025-11-09 00:00:00 +00:00
Oneric
631f0e817b Revert "ci: allow docs to build on all runners"
This reverts commit 9f05b19b6b.
We download a amd64 bild of the scaleway CLI.
2025-11-09 00:00:00 +00:00
949d641715 Merge pull request 'ci: try to parallelise test jobs' (#1003) from Oneric/akkoma:ci-parallel-testing into develop
Reviewed-on: AkkomaGang/akkoma#1003
2025-11-09 13:09:31 +00:00
Oneric
9b99a7f902 cachex: reduce default user and object cache lifetime
This caches query results from our own database, but does not respect
transaction boundaries or syncs to transaction success/failure. Long
lifetimes increase the chance of such desync occuring and being written
back to the database, see: AkkomaGang/akkoma#956

Until 1d02a9a35dfbf8bbfc369e8ae8712bbce34ff073 recently fixed it
this used a 3 second lifetime anyway, so this won’t result in
performance degradations but hopefully prevents a rise in desyncs.
2025-11-09 00:00:00 +00:00
Oneric
9f05b19b6b ci: allow docs to build on all runners
The docker image already offers variants for all our runner arches
2025-11-09 00:00:00 +00:00
Oneric
74cfb2e0db cosmetic/app: use appropriate timer function instead of scaling up lower res units 2025-11-09 00:00:00 +00:00
Oneric
dcd664ffbc ci: dedupe build+release job definitions
If releasr images provide amd64 and arm64 in the same tag
this can be slightly simplified further.
2025-11-09 00:00:00 +00:00
Oneric
534124cae2 mix/deps: upgrade cachex to 4.x 2025-11-09 00:00:00 +00:00
Oneric
c3195b2011 ci: move one test job to arm64 to allow parallel execution 2025-11-09 00:00:00 +00:00
Oneric
d1050dab76 Fix cachex TTL options
For some reason most caches were never updated to
the new setting format introduced in the 3.0 release.
2025-11-09 00:00:00 +00:00
Oneric
dc95f95738 mix/deps: upgrade timex
Unlocked by the gettext upgrade
2025-11-09 00:00:00 +00:00
Oneric
086d0a052b mix/deps: upgrade to new gettext API
This supposedly improves compile times
and also unlocks a minor timex upgrade.
1.0.0 was released without API changes after the 0.26 series.
2025-11-09 00:00:00 +00:00
Oneric
54fd8379ad web/gettext: split Gettext Backend and additional utility functions
This avoids the importing the heavy Gettext backend in some places
and makes it clearer what’s actually used in the project and what’s
used by the Gettext library.
With the to-be-pulled-in Gettext API change this split
will be even more helpful for code clarity.

As a bonus documentation is improved and
the unused locale_or_default function removed.
2025-11-09 00:00:00 +00:00
Oneric
b344c80ad2 cosmetic/app: dedupe always added task chhildren
As a side effect this gets rid of a compiler warning
about the prior task_children(:test) function clause
being dead code in non-test builds
2025-11-09 00:00:00 +00:00
Oneric
264202c7b3 mix/deps: upgrade dialyxir patch version 2025-11-09 00:00:00 +00:00
Oneric
4e321f4f47 mix/deps: upgrade elixir_argon2 to 4.x series
Only the default parameters changed from 3.x to 4.x.
It now matches the proposed defaults suggested in the RFC
for constrained environments.
The prior defaults have worse latency, but probably slightly better
security while spawning fewer threads, so let’s stick with them.

By setting the defaults in the config instance owners
can (continue to) tweak these for the specific setup.
2025-11-09 00:00:00 +00:00
Oneric
be8846bd89 mix/deps: upgrade telemetry_metrics to 1.x series
The 0.x series was promoted to 1.x without API changes
to indicate stability of the interface.
2025-11-09 00:00:00 +00:00
Oneric
b1397e1670 mix/deps: force minor updates not happening automatically
earmark is intentionally excluded due to a bug in current releases
starting with 1.4.47 affecting our tests. Admittedly it was also bugged
before but in a different way.

See: https://github.com/pragdave/earmark/issues/361#issuecomment-3494355774
2025-11-09 00:00:00 +00:00
Oneric
0afe1ab4b0 Resolve Phoenix 1.8 deprecations 2025-11-09 00:00:00 +00:00
Oneric
57dc812b70 mix/deps: upgrade phoenix family 2025-11-09 00:00:00 +00:00
Oneric
e91d7c3291 mix/deps: follow branches of git repos
To allow easy updates via  later on.
The only actual new changes pulled in in this commit are
build tweaks to pleroma’s captcha library
2025-11-09 00:00:00 +00:00
Oneric
bf5e2f205e mix/deps: allow ueberauth updates again
e2f749b5b0 pinned the version to avoid
a partiuclar broken release, but the issue has since been fixed.
See: https://github.com/ueberauth/ueberauth/issues/194
2025-11-09 00:00:00 +00:00
Oneric
53f866b583 mix/deps: upgrade everything to compatible newer versions
Or at least they were supposed to be compatible.
Due to an ecto optimisation change it is now illegal to use
replace_all_except if the union of conflict_targets and the fields
exempted from updating are equal to _all_ fields of this table.
See: https://github.com/elixir-ecto/ecto/issues/4633
2025-11-09 00:00:00 +00:00
467e75e3b1 Merge pull request 'api: prefer duration param for mute expiration' (#1004) from Oneric/akkoma:mute-expiry-duration into develop
Reviewed-on: AkkomaGang/akkoma#1004
2025-11-08 13:39:55 +00:00
8532f789ac api: prefer duration param for mute expiration
Mastodon 3.3 added support for temproary mutes but uses "duration"
instead of our older "expires_in". Even Husky only sets "duration"
nowadays.

Signed-off-by: marcin mikołajczak <git@mkljczk.pl>

Cherry-picked-from: 5d3d6a58f7
2025-11-08 00:00:00 +00:00
679c4e1403 Merge pull request 'api: return error when replying to already deleted post' (#1001) from Oneric/akkoma:replying-to-a-ghost into develop
Reviewed-on: AkkomaGang/akkoma#1001
2025-11-06 16:46:57 +00:00
Oneric
d635a39141 api: return error when replying to already deleted post
Of course the aprent post might still be deleted after the reply was
already created, but in this case the reply will still show up as a
reply and be federated as a reply with a reference to the parent post.
If the parent was already deleted before the reply gets created however
it used to be indistinguishable from a root post both in Masto API and
ActivityPub.

From a UX perspective, users likely will like to know if the post
they’re replying to no longer exists by the time they finished writing.
The natural language error will show up in akkoma-fe without clearing
the post form, meaning users can decide to discard the reply or copy it
to post as a new root post. It seems sensibly to for other clients to
behave like this too, but so far no more clients were actually tested.

Furthermore, this used to allow replying to all sorts of activities not
just posts which was rather non-sensical (and after all processsing
steps turned into a reply to the object referenced by the activity).
In particular this allowed replying to an user object by specifying the
db ID of a follow request activity (if the latter was somehow obtained).

Note: empty-string in_reply_to parameters are explicitly ignored since
45ebc8dd9a to workaround one buggy client;
see: https://git.pleroma.social/pleroma/pleroma/-/issues/355.
It’s not clear if this workaround is still necessary,
but it is preserved by this commit.

Resolves: AkkomaGang/akkoma#522
2025-11-06 15:58:40 +01:00
8da0828b4a Merge pull request 'reload emoji asynchronously and optimise emoji updates' (#998) from Oneric/akkoma:async-emoji-reload into develop
Reviewed-on: AkkomaGang/akkoma#998
2025-11-06 14:55:56 +00:00
ccde26725f Merge pull request 'api_spec/cast: iteratively retry to clean all offending parameters' (#995) from Oneric/akkoma:apispec-cast-multitolerance into develop
Reviewed-on: AkkomaGang/akkoma#995
2025-11-06 14:55:38 +00:00
Oneric
7e6efb2356 api_spec/cast: iteratively retry to clean all offending parameters
While the function signature allows returning many errors at once,
OpenApiSpex.cast_and_validate currently only ever returns the first
invalid field it encounters. Thus we need to retry multiple times to
clean up all offenders.

Fixes: AkkomaGang/akkoma#992 (comment)
2025-11-05 00:00:00 +00:00
Oneric
bd6dda2cd0 docs: fix multi-paragraph list items
Markdown requires an indentation of 4 for a following paragraph to
continue a list item. Previously, the continuing paragraphs were only
indented by 2 spaces, leading to the list being interrupted and
numbering restarted each time.
2025-11-02 00:00:00 +00:00
Oneric
e7d76bb194 emoji: reload asynchronously
No caller of `reload` actually uses the result in any way
so there’s no need to wait for a response and risk running
into a timeout (by default 5 seconds).

Discovered-by: sn0w <me@sn0w.re>
Based-on: 1fb54d5c2c
2025-10-30 00:00:00 +00:00
Oneric
318ee6ee17 emoji: avoid full reloads when possible
Reloading the entire emoji set from disk, reparsing all pack JSON files,
etc is unnecessarily costly for large emoji sets. We already know which
single or few changes we want to apply, so do just that instead.
2025-10-29 00:00:00 +00:00
Oneric
f86a88ca19 emoji: store in unordered set
No caller cares about the order
(and although, rare with concurrent reads at same time like a write
the table might return unordered results anyway).
Unordered sets have a constant read time,
ordered sets logarithmic times, but there’s no benfit for us
2025-10-29 00:00:00 +00:00
Oneric
d38ca268c4 cosmetic/emoji: fix misleading docs and var names 2025-10-29 00:00:00 +00:00
Oneric
0cb2807667 emoji/pack: fix newly created pack having nil name
At the next reload the name was already set to the directory name,
but if using the created pack directly issues arose.
2025-10-29 00:00:00 +00:00
862fba5ac5 Merge pull request 'Do not try to redirect to post display URLs for non-Create activities' (#997) from Oneric/akkoma:fix-non-create-html-redirect into develop
Reviewed-on: AkkomaGang/akkoma#997
2025-10-26 12:45:22 +00:00
Oneric
47ac4ee817 Do not try to redirect to post display URLs for non-Create activities
Display will fail for all but Create and Announce anyway since
0c9bb0594a. We exclude Announce
activities from redirects here since they are not identical
with the announced post and akkoma-fe stripping the repeat header
on he /notice/ page might lead to confusion about which is which.

In particular those redirects exiting breaks the assumptions from
the above commit’s commit message and made it possible to obtain
database IDs for activities other than one’s own likes allowing
slightly more mischief with the rendering bug it fixed.

Note: while 0c9bb0594a speculated about
public likes also leaking IDs to other users, the public like endpoint
is actually paginated by post id/date not like id/date like the private
endpoint. Thus it does not allow getting database IDs of others’ likes.
2025-10-26 00:00:00 +00:00
9d12c7c00c Merge pull request 'Preserve mastodon-style quote-fallback marker' (#977) from mastodon-quotes into develop
Reviewed-on: AkkomaGang/akkoma#977
Reviewed-by: Oneric <oneric@noreply.akkoma>
2025-10-24 21:51:49 +00:00
Oneric
b7107a9e33 docs: adjust for include_types deprecation
And replace an overlooked usage in tests.
Fixes omissions in b3a0833d30
2025-10-24 00:00:00 +00:00
8857c98eaf Merge pull request 'Use types for filtering notifications' (#993) from mkljczk/akkoma:akkoma-notification-types into develop
Reviewed-on: AkkomaGang/akkoma#993
Reviewed-by: Oneric <oneric@noreply.akkoma>
2025-10-24 19:05:27 +00:00
c3dd582659 Allow mastodon-style quotes 2025-10-24 00:00:00 +00:00
b3a0833d30 Use types for filtering notifications
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
2025-10-15 09:06:43 +02:00
300302d432 Merge pull request 'Treat known quotes and replies as such even if parent unavailable' (#991) from Oneric/akkoma:replies-unknown into develop
Reviewed-on: AkkomaGang/akkoma#991
2025-10-13 12:24:27 +00:00
Oneric
0907521971 Treat known quotes and replies as such even if parent unavailable
Happens commonly for e.g. replies to follower-only posts
if no one one your instance follows the replied-to account
or replies/quotes of deleted posts.
Before this change Masto API response would treat those
replies as root posts, making it hard to automatically or
mentally filter them out.

With this change replies already show up sensibly as
recognisable  replies in akkoma-fe.
Quotes of unavailable posts however still show up as if they
weren’t quotes at all, but this can only be improved client-side.

Fixes: AkkomaGang/akkoma#715
2025-10-13 10:26:57 +00:00
Weblate
4744ae4328 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 99.0% (106 of 107 strings)

Co-authored-by: Poesty Li <poesty7450@gmail.com>
Co-authored-by: Weblate <noreply@weblate.org>
Translate-URL: http://translate.akkoma.dev/projects/akkoma/akkoma-backend-errors/zh_Hans/
Translation: Pleroma fe/Akkoma Backend (Errors)
2025-10-13 10:01:37 +00:00
Weblate
2b076e59c1 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (1004 of 1004 strings)

Co-authored-by: Poesty Li <poesty7450@gmail.com>
Co-authored-by: Weblate <noreply@weblate.org>
Translate-URL: http://translate.akkoma.dev/projects/akkoma/akkoma-backend-config-descriptions/zh_Hans/
Translation: Pleroma fe/Akkoma Backend (Config Descriptions)
2025-10-13 10:01:37 +00:00
40ef08d46c bump version 2025-10-13 11:01:10 +01:00
Oneric
01a728464f changelog: add missing entries 2025-10-12 00:00:00 +00:00
feef5f7f6f Merge pull request 'Default to HTTP1 again and no pool options for individual requests' (#988) from Oneric/akkoma:no-http2-4u into develop
Reviewed-on: AkkomaGang/akkoma#988
Reviewed-by: floatingghost <hannah@coffee-and-dreams.uk>
2025-10-10 20:44:54 +00:00
62856efb5c Merge pull request 'Delete more unused or useless bits' (#987) from Oneric/akkoma:delete-unused into develop
Reviewed-on: AkkomaGang/akkoma#987
Reviewed-by: floatingghost <hannah@coffee-and-dreams.uk>
2025-10-10 16:18:13 +00:00
Oneric
6878e4de60 reverse_proxy: don't trust header about body size
Except of course for HEAD requests
2025-10-10 00:00:00 +00:00
Oneric
ed5d609ba4 http: do not add adapter pool options to individual requests
They do nothing. As documented[1] only three specific
options regarding timeouts are parsed for individual request
and none of them is set by AdapterHelper, only pool-specific options.

In particular this means we always relied on Mint’s default CA cert
verification based on queries to the CAStore package (which we include).

[1]: https://hexdocs.pm/finch/Finch.html#request/3-options
2025-10-10 00:00:00 +00:00
Oneric
c94a3b10ee Delete barely used logger mock
It required a bunch of and even call-specific boilerplate
and is not necessary since we can just capture the real logger
as laready done in other tests.
2025-10-10 00:00:00 +00:00
Oneric
203f5a89a2 config: revert to HTTP1-only for outgoing requests
Due to bugs in Finch discussed in
AkkomaGang/akkoma#978
and AkkomaGang/akkoma#980
allowing both HTTP versions ends up breaking
some requests if they have a body.

Fixes: AkkomaGang/akkoma#978
2025-10-10 00:00:00 +00:00
Oneric
f4e188af0a Delete useless, custom JobQueueMonitor
While its data was included in healthcheck responses,
it was not used to determine the healthy status
and for informational purposes Prometheus metrics,
ObanWeb dashboard or the Phoenix live dashboard are all better fits.
In particular, the data shown in healtcheck responses had no temporal
information, but there’s quite a difference between X failures scattered
across many days of uptime and X failures within a couple minutes.
2025-10-10 00:00:00 +00:00
6dc546eda7 Merge pull request 'api: order follow requests by date of request' (#984) from Oneric/akkoma:followreq-order into develop
Reviewed-on: AkkomaGang/akkoma#984
2025-10-09 23:55:09 +00:00
6fbc8330db Merge pull request 'Fix some typos and remove unused code' (#985) from mkljczk/akkoma:akkoma-typos into develop
Reviewed-on: AkkomaGang/akkoma#985
Reviewed-by: Oneric <oneric@noreply.akkoma>
2025-10-09 21:53:03 +00:00
9d8583314b Repesct :restrict_unauthenticated for hashtag rss/atom feeds
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
2025-10-09 15:29:26 +02:00
794b2cde45 Fix some typos and remove unused code
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
2025-10-09 15:26:35 +02:00
Oneric
0c9bb0594a Restrict Masto statuses API to Create and Announce activities
If the id of another activity type was used
it would show the post referenced by the activity
but wrongly attributing it to the activity actor
instead of the actual author.

E.g. ids of like activities can be obtained from
pagiantion info of the favourites endpoint.
For all other activity types the id would need to be guessed
which is considered practically infeasible for Flake UUIDs.

This should’ve been mostly harmless in practice, since:
  - since the activity has the same context as the original post,
    both the original and misattributed duplicate will show up in the
    same thread
  - only posts liked by a user can be misattributed to them,
    presumably making it hard/impossible to associate someone with
    content they disagree with
  - by default only the liking user themself can view their like history
    and therefore obtain IDs for their like activities.
    Notably though, there is a user seting to allow anyone to browse
    ones like history and therefore obtain like IDs. However, since
    akkoma-fe has no support for actually displaying those, there might
    be no actual users of this features.
2025-10-09 00:00:00 +00:00
Oneric
06a0cf4278 api: order follow requests by date of request
This is more intuitive for users.
On the flip side, this makes the API more confusing
since now min_id/max_id no longer correspond to ids returned in the
response and only link headers can be used to traverse all response
pages. However, Mastodon already does this according to its
documentation, so clients should already handle this well.

The only other usage of get_follow_requests_query is only interested
in the total count of requests (from active users), thus changing its
select part is safe.
Also gets rid of (outside tests unused) User.get_follow_requests.

Fixes: AkkomaGang/akkoma#380
2025-10-09 00:00:00 +00:00
Oneric
521dfa4670 Use keyed lists for pagination with foreign id
The old approach required adding a special virtual field
to any table potentially needing such foreign-id pagination and
also still required manually sorting according to pagiantion settings
since the pagination helper does not know whether
this virtual field was set or not.

Using lists with each entry containing the pagination id and the actual
entry insterad allows any table to use this mechanism unchanged and
does not require manually sorting.

Since it was unused, this also drops the pagination mode paramter from
fetch_favourited_with_fav_id.

Furthermore, as a side effect of this change a bug in the favourite
benchmark is fixed. It used to incorrectly attempt to use IDs of
the liked objects for pagination instead of the like IDs as advertised
in Link headers.
2025-10-09 00:00:00 +00:00
24d828d9a6 Merge pull request 'telemetry: expose count of currently pending/scheduled jobs per queue' (#982) from Oneric/akkoma:telemetry-job-queue-pending into develop
Reviewed-on: AkkomaGang/akkoma#982
2025-10-04 22:44:50 +00:00
2190f3bede Merge pull request 'Do not federate undo->block activities' (#958) from undo-block-federation into develop
Reviewed-on: AkkomaGang/akkoma#958
Reviewed-by: Oneric <oneric@noreply.akkoma>
2025-10-04 22:43:50 +00:00
8c33eed93e Merge pull request 'Renew HTTP signatures when following redirects' (#973) from Oneric/akkoma:httpsig_redirect_resign into develop
Reviewed-on: AkkomaGang/akkoma#973
2025-10-04 16:25:49 +00:00
1c74fcb35c Merge pull request 'optimize follow_request_count for own account view' (#981) from mkljczk/akkoma:optimize-follow-request-count into develop
Reviewed-on: AkkomaGang/akkoma#981
2025-10-04 10:48:12 +00:00
Oneric
43d4716b5a telemetry: expose count of currently pending jobs per queue
Split into scheduled (intentionally delayed until a later trigger date)
and available (eligible for immediate processing but did not yet start).
This will help in diagnosing overloaded instances or too-low queue
limits as well as expose configuration mishaps like
AkkomaGang/akkoma#924.
(The latter by violently crashing the telemetry poller process while
attempting put_in for a non-configured queue creating well visible logs)
2025-10-03 00:00:00 +00:00
545a83a5f1 optimize follow_request_count for own account view
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
2025-09-28 23:59:24 +02:00
Oneric
b1e5dda26a telemetry: reduce polling frequency for periodic measurements
Instancce stats are cached only renewed every 5 minutes anyway
and IO stats are cumulative over the entire runtime so no info
is lost.
Polling those every 10s is wasteful and the next commit will add a
periodic measurement which is (comparetively) more costly to compute.
2025-09-21 00:00:00 +00:00
Oneric
b9f79f333f changelog: document changes wrt to http option config keys 2025-09-07 00:00:00 +00:00
Oneric
cb19d3285a Drop superfluous RequestBuilder
It’s only used in one place and there not even all of
its functionality is needed. It’s not only simpler and shorter,
but easier to understand if Tesla’s keyword list is just inlined.

The only useful bit which is now migrated to Pleroma.HTTP is
addition of the user-agent header (except, sometimes, in tests)
2025-09-07 00:00:00 +00:00
Oneric
c607387b4a http: do not mix and duplicate Tesla opts into adapter opts 2025-09-07 00:00:00 +00:00
Oneric
271d7d14d4 media_proxy: use :head atom instead of binary
It automatically uses correct capitalisation and is the preferred form.
2025-09-07 00:00:00 +00:00
Oneric
7f9e898781 reverse_proxy: delete unused dynamic client
Since 364b6969eb the reverse proxy
is fixed to use the default HTTP module and all these modules
have been unused since
2025-09-07 00:00:00 +00:00
Oneric
882d8e0320 http/tzdata: ignore unsupported opts
Tzdata assumes Hackney opt names and only uses it to
enable following redirects which we already do anyway.
2025-09-07 00:00:00 +00:00
Oneric
a95b0a5d61 http/webpush: ignore opts due to incompatible format
The web_push_encryption lib assumes HTTPoison semantics
which is why we also need to convert the header format.
Inspecting the libraries source shows that Tesla won’t
understand the options anyway and its only used to enable TLS/SSL.
2025-09-07 00:00:00 +00:00
Oneric
5d59cb7ac3 rel_me: drop unsupported http option
Enforcing a hard response body limit is currently not possible with
Finch. Presumably a leftover from when multiple backends were supported.
2025-09-07 00:00:00 +00:00
Oneric
d34f6ebcdd rich_media/helpers: drop unsupported http opts
When this was ported from Pleroma in
5da9cbd8a5
we did not take into acount that Akkoma’s and Pleroma’s
HTTP backend take different options.
There’s no need for the :pool option
and enforcing a body limit on download
is currently not possible with Finch
2025-09-07 00:00:00 +00:00
Oneric
6d54bd95bc changelog: add entry for re-sign on redirect 2025-09-07 00:00:00 +00:00
Oneric
2b4b68eba7 Ensure private keys are not logged
Ideally we’d use a single common HTTP request error format handling
for _all_ HTTP requests (including non-ActivityPub requests, e.g. NodeInfo).
But for the purpose of this commit this would create too much noise
and it is significant effort to go through all error pattern matches etc
too ensure it is still all correct or update as needed.
2025-09-07 00:00:00 +00:00
Oneric
ff46e448c8 refactor: move creation of date strings for signatures into plug
The Signature module now handles interaction with the HTTPSignature library
and the plug everything related to HTTP itself. It now also no longer needs to be public.
2025-09-06 00:00:00 +00:00
Oneric
4c4982d611 Re-sign requests when following redirects
To achieve this signatures are now generated by a custom
Tesla Middleware placed after the FollowRedirects Middleware.
Any requests which should be signed needs
to pass the signing key via opts.

This also unifies the associated header logic between fetching and
publishing, notably resolving a divergence wrt the "host" header.
Relevant spec demands the host header shall include a port
identification if not using the protocols standard port.

Fixes: AkkomaGang/akkoma#731
2025-09-06 00:00:00 +00:00
290171d006 don't care about the type as long as it has an attachment 2025-08-04 18:29:46 +01:00
4cfbef3b09 fix from_url 2025-08-04 18:20:33 +01:00
27eaddaa05 inspect the whole data on dry run 2025-08-04 18:07:07 +01:00
31e25efbfb include previously missed formats 2025-08-04 18:02:58 +01:00
c588c3f674 remove inspect 2025-08-03 23:18:40 +01:00
b5e17f131a correct pipeline 2025-08-03 23:17:55 +01:00
2c34bb32ca rewrite media domains mix task 2025-08-03 23:06:41 +01:00
eca7ed572b Do not federate undo->block activities
the :do_not_federate checks were omitted from the undo pipeline,
which could lead to them federating.

this commit enforces :outgoing_blocks on undos as well - and
any existing value of :do_not_federate is preserved, if one exists

Fixes #957
2025-08-02 09:47:32 +01:00
411 changed files with 16015 additions and 10506 deletions

View file

@ -1,110 +0,0 @@
labels:
platform: linux/amd64
when:
event:
- push
- tag
branch:
- develop
- stable
variables:
- &scw-secrets
SCW_ACCESS_KEY:
from_secret: SCW_ACCESS_KEY
SCW_SECRET_KEY:
from_secret: SCW_SECRET_KEY
SCW_DEFAULT_ORGANIZATION_ID:
from_secret: SCW_DEFAULT_ORGANIZATION_ID
- &setup-hex "mix local.hex --force && mix local.rebar --force"
- &on-stable
when:
event:
- push
- tag
branch:
- stable
- &tag-build "export BUILD_TAG=$${CI_COMMIT_TAG:-\"$CI_COMMIT_BRANCH\"} && export PLEROMA_BUILD_BRANCH=$BUILD_TAG"
- &clean "(rm -rf release || true) && (rm -rf _build || true) && (rm -rf /root/.mix)"
- &mix-clean "mix deps.clean --all && mix clean"
steps:
# Canonical amd64
debian-bookworm:
image: hexpm/elixir:1.15.4-erlang-26.0.2-debian-bookworm-20230612
environment:
MIX_ENV: prod
DEBIAN_FRONTEND: noninteractive
commands:
- apt-get update && apt-get install -y cmake libmagic-dev rclone zip imagemagick libmagic-dev git build-essential g++ wget
- *clean
- echo "import Config" > config/prod.secret.exs
- *setup-hex
- *tag-build
- mix deps.get --only prod
- mix release --path release
- zip akkoma-amd64.zip -r release
release-debian-bookworm:
image: akkoma/releaser
environment: *scw-secrets
commands:
- export SOURCE=akkoma-amd64.zip
# AMD64
- export DEST=scaleway:akkoma-updates/$${CI_COMMIT_TAG:-"$CI_COMMIT_BRANCH"}/akkoma-amd64.zip
- /bin/sh /entrypoint.sh
# Ubuntu jammy (currently compatible)
- export DEST=scaleway:akkoma-updates/$${CI_COMMIT_TAG:-"$CI_COMMIT_BRANCH"}/akkoma-amd64-ubuntu-jammy.zip
- /bin/sh /entrypoint.sh
debian-bullseye:
image: hexpm/elixir:1.15.4-erlang-26.0.2-debian-bullseye-20230612
environment:
MIX_ENV: prod
DEBIAN_FRONTEND: noninteractive
commands:
- apt-get update && apt-get install -y cmake libmagic-dev rclone zip imagemagick libmagic-dev git build-essential g++ wget
- *clean
- echo "import Config" > config/prod.secret.exs
- *setup-hex
- *tag-build
- mix deps.get --only prod
- mix release --path release
- zip akkoma-amd64-debian-bullseye.zip -r release
release-debian-bullseye:
image: akkoma/releaser
environment: *scw-secrets
commands:
- export SOURCE=akkoma-amd64-debian-bullseye.zip
# AMD64
- export DEST=scaleway:akkoma-updates/$${CI_COMMIT_TAG:-"$CI_COMMIT_BRANCH"}/akkoma-amd64-debian-bullseye.zip
- /bin/sh /entrypoint.sh
# Canonical amd64-musl
musl:
image: hexpm/elixir:1.15.4-erlang-26.0.2-alpine-3.18.2
<<: *on-stable
environment:
MIX_ENV: prod
commands:
- apk add git gcc g++ musl-dev make cmake file-dev rclone wget zip imagemagick
- *clean
- *setup-hex
- *mix-clean
- *tag-build
- mix deps.get --only prod
- mix release --path release
- zip akkoma-amd64-musl.zip -r release
release-musl:
image: akkoma/releaser
<<: *on-stable
environment: *scw-secrets
commands:
- export SOURCE=akkoma-amd64-musl.zip
- export DEST=scaleway:akkoma-updates/$${CI_COMMIT_TAG:-"$CI_COMMIT_BRANCH"}/akkoma-amd64-musl.zip
- /bin/sh /entrypoint.sh

View file

@ -1,84 +0,0 @@
labels:
platform: linux/arm64
when:
event:
- push
- tag
branch:
- develop
- stable
variables:
- &scw-secrets
SCW_ACCESS_KEY:
from_secret: SCW_ACCESS_KEY
SCW_SECRET_KEY:
from_secret: SCW_SECRET_KEY
SCW_DEFAULT_ORGANIZATION_ID:
from_secret: SCW_DEFAULT_ORGANIZATION_ID
- &setup-hex "mix local.hex --force && mix local.rebar --force"
- &on-stable
when:
event:
- push
- tag
branch:
- stable
- &tag-build "export BUILD_TAG=$${CI_COMMIT_TAG:-\"$CI_COMMIT_BRANCH\"} && export PLEROMA_BUILD_BRANCH=$BUILD_TAG"
- &clean "(rm -rf release || true) && (rm -rf _build || true) && (rm -rf /root/.mix)"
- &mix-clean "mix deps.clean --all && mix clean"
steps:
# Canonical arm64
debian-bookworm:
image: hexpm/elixir:1.15.4-erlang-26.0.2-debian-bookworm-20230612
environment:
MIX_ENV: prod
DEBIAN_FRONTEND: noninteractive
commands:
- apt-get update && apt-get install -y cmake libmagic-dev rclone zip imagemagick libmagic-dev git build-essential g++ wget
- *clean
- echo "import Config" > config/prod.secret.exs
- *setup-hex
- *tag-build
- mix deps.get --only prod
- mix release --path release
- zip akkoma-arm64.zip -r release
release-debian-bookworm:
image: akkoma/releaser:arm64
environment: *scw-secrets
commands:
- export SOURCE=akkoma-arm64.zip
- export DEST=scaleway:akkoma-updates/$${CI_COMMIT_TAG:-"$CI_COMMIT_BRANCH"}/akkoma-arm64-ubuntu-jammy.zip
- /bin/sh /entrypoint.sh
- export DEST=scaleway:akkoma-updates/$${CI_COMMIT_TAG:-"$CI_COMMIT_BRANCH"}/akkoma-arm64.zip
- /bin/sh /entrypoint.sh
# Canonical arm64-musl
musl:
image: hexpm/elixir:1.15.4-erlang-26.0.2-alpine-3.18.2
<<: *on-stable
environment:
MIX_ENV: prod
commands:
- apk add git gcc g++ musl-dev make cmake file-dev rclone wget zip imagemagick
- *clean
- *setup-hex
- *mix-clean
- *tag-build
- mix deps.get --only prod
- mix release --path release
- zip akkoma-arm64-musl.zip -r release
release-musl:
image: akkoma/releaser:arm64
<<: *on-stable
environment: *scw-secrets
commands:
- export SOURCE=akkoma-arm64-musl.zip
- export DEST=scaleway:akkoma-updates/$${CI_COMMIT_TAG:-"$CI_COMMIT_BRANCH"}/akkoma-arm64-musl.zip
- /bin/sh /entrypoint.sh

View file

@ -1,8 +1,5 @@
labels:
platform: linux/amd64
depends_on:
- build-amd64
platform: linux/arm64
when:
event:
@ -24,8 +21,8 @@ steps:
image: python:3.10-slim
commands:
- apt-get update && apt-get install -y rclone wget git zip
- wget https://github.com/scaleway/scaleway-cli/releases/download/v2.5.1/scaleway-cli_2.5.1_linux_amd64
- mv scaleway-cli_2.5.1_linux_amd64 scaleway-cli
- wget https://github.com/scaleway/scaleway-cli/releases/download/v2.5.1/scaleway-cli_2.5.1_linux_arm64
- mv scaleway-cli_2.5.1_linux_arm64 scaleway-cli
- chmod +x scaleway-cli
- ./scaleway-cli object config install type=rclone
- cd docs

104
.woodpecker/publish.yml Normal file
View file

@ -0,0 +1,104 @@
when:
event:
- push
- tag
branch:
- develop
- stable
evaluate: 'SKIP_DEVELOP != "YES" || CI_COMMIT_BRANCH != "develop"'
matrix:
include:
# Canonical amd64
- ARCH: amd64
SUFFIX:
IMG_VAR: debian-bookworm-20230612
UBUNTU_EXPORT: YES
# old debian variant of amd64
- ARCH: amd64
SUFFIX: -debian-bullseye
IMG_VAR: debian-bullseye-20230612
# Canonical amd64-musl
- ARCH: amd64
SUFFIX: -musl
IMG_VAR: alpine-3.18.2
SKIP_DEVELOP: YES
# Canonical arm64
- ARCH: arm64
SUFFIX:
RELEASER_TAG: :arm64
IMG_VAR: debian-bookworm-20230612
UBUNTU_EXPORT: YES
# Canonical arm64-musl
- ARCH: arm64
SUFFIX: -musl
RELEASER_TAG: :arm64
IMG_VAR: alpine-3.18.2
SKIP_DEVELOP: YES
labels:
platform: linux/${ARCH}
steps:
# Canonical amd64
build:
image: hexpm/elixir:1.15.4-erlang-26.0.2-${IMG_VAR}
environment:
MIX_ENV: prod
DEBIAN_FRONTEND: noninteractive
commands: |
# install deps
case "${IMG_VAR}" in
debian*)
apt-get update && apt-get install -y \
cmake libmagic-dev rclone zip git wget \
build-essential g++ imagemagick libmagic-dev
;;
alpine*)
apk add git gcc g++ musl-dev make cmake file-dev rclone wget zip imagemagick
;;
*)
echo "No package manager defined for ${BASE_IMG}!"
exit 1
esac
# clean leftovers
rm -rf release
rm -rf _build
rm -rf /root/.mix
# setup
echo "import Config" > config/prod.secret.exs
mix local.hex --force
mix local.rebar --force
export BUILD_TAG=$${CI_COMMIT_TAG:-\"$CI_COMMIT_BRANCH\"}
export PLEROMA_BUILD_BRANCH=$BUILD_TAG
# actually build and zip up
mix deps.get --only prod
mix release --path release
zip akkoma-${ARCH}${SUFFIX}.zip -r release
release:
image: akkoma/releaser${RELEASER_TAG}
environment:
SCW_ACCESS_KEY:
from_secret: SCW_ACCESS_KEY
SCW_SECRET_KEY:
from_secret: SCW_SECRET_KEY
SCW_DEFAULT_ORGANIZATION_ID:
from_secret: SCW_DEFAULT_ORGANIZATION_ID
commands: |
export SOURCE=akkoma-${ARCH}${SUFFIX}.zip
export DEST=scaleway:akkoma-updates/$${CI_COMMIT_TAG:-"$CI_COMMIT_BRANCH"}/$${SOURCE}
/bin/sh /entrypoint.sh
if [ "${UBUNTU_EXPORT}" = "YES" ] ; then
# Ubuntu jammy (currently compatible with our default debian builds)
export DEST=scaleway:akkoma-updates/$${CI_COMMIT_TAG:-"$CI_COMMIT_BRANCH"}/akkoma-${ARCH}-ubuntu-jammy.zip
/bin/sh /entrypoint.sh
fi

View file

@ -1,18 +1,20 @@
labels:
platform: linux/amd64
when:
- event: pull_request
matrix:
# test the lowest and highest versions
include:
- ELIXIR_VERSION: 1.14
- ELIXIR_VERSION: 1.15
OTP_VERSION: 25
LINT: NO
- ELIXIR_VERSION: 1.18
OTP_VERSION: 27
PLATFORM: linux/amd64
- ELIXIR_VERSION: 1.19
OTP_VERSION: 28
LINT: YES
PLATFORM: linux/arm64
labels:
platform: ${PLATFORM}
services:
postgres:
@ -33,6 +35,7 @@ steps:
DB_HOST: postgres
LINT: ${LINT}
commands:
- sh -c 'uname -a && cat /etc/os-release || :'
- mix local.hex --force
- mix local.rebar --force
- mix deps.get

View file

@ -6,9 +6,225 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## Unreleased
### Update note
- If you are using database search with a non-default RUM index,
you _MUST_ apply the new optional RUM migration before upgrading.
Then after upgrading you wil need to refresh your RUM index setup
to also get the new search behaviour. This can be done by "changing"
your text search config to your current value (or something else, like `simple` if you so wish)
via the `database set_text_search_config <value>` mix task
### Removed
- vestige C2S access to follow* collections was dropped
### Added
- federated voter count of polls is now parsed and federated out too;
this fixes vote percetanges for new and refreshed remote multi-selection polls
- new config options to restrict unauthenticated search API access under `:pleroma, :restrict_unauthenticated, :search`
- extended MFM support further
### Fixed
- fixed status search not respecting `resolve=false`
- fixed later search result pages again fetching remote content
- handle reports referring to a single plain id as their object;
affected e.g. JSON-LD compacted reports without status references from Iceshrimp.NET
- fixed several issues parsing remote Question objects
- fixed signatures from blocked or deleted actors still being accepted
- fixed follow* collections being readable without signature even if authorized_fetch mode is enabled
- fixed ones own follow* counts sometimes being redacted in API if hiding count for others
- fixed delete&redraft still deleting attachment files most oft the time once the initial redraft delay elapsed
- fixed fetched objects not normalising various valid forms of the public addressing URI
- fixed searches for an already known statuses with resolve=true sometimes failing
if searching by a display URL instead of canonical AP ID.
With resolve=false no display URL lookups are possible.
- fixed single-selection poll states not updating when receiving an Update activity for the status
- fixed fetched updates of statuses with a poll or rediscovered pruned objects not being passed through MRFs
- fixed potential data inconsistencies and API ordering for rediscovered partially pruned objects
- fixed tagged (mentioned) but not addressed users receiving notifications about
statuses they are not actually allowed to access
- fixed tranlator service being queried for supported languages even if not enabled
- fixed explicitly static media proxy previews pointing to a broken redirect for non-animated files
### Changed
- New installations (not existing instances) now default to the `simple` full-text-search config
- Unauthenticated search requests now by default force-disable remote fetches and pagination
- Post search can now match text in the content warning with the database provider
- prefixing a user search query with `@` will limit results to matching nicknames only, if the query contains no enclosed whitespace
## 2026.05 (3.19.0)
### General note
- backup restore instructions very slightly changed but in an important way.
It is no longer necessary to force sequential, single-transaction mode.
Now indexes can and are recommended to be restored in parallel significantly speeding up the overall process.
*(If ignoring instructions and using parallel mode before we got rid of index interdependence, `pg_restore` started restoring some indexes before other indexes they heavily depend on were done. Ironically leading to **much** worse restore times than pure sequential mode)*
### Removed
- as announced in 2025.12 (3.17) and since no complaints were raised, the semi-broken, seemingly unused and improvement-blocking thread containment feature is now removed. This entails the following API changes:
- dropped `PATCH /api/v1/accounts/update_credentials` input parameter `skip_thread_containment`
- dropped `GET /api/v1/accounts/:id` response key `pleroma.skip_thread_containment`
- dropped `GET /api/v1/pleroma/admin/users/:nickname/credentials` response key `skip_thread_containment`
- dropped the `skipThreadContainment` key from nodeinfos `metadata` object
- for the same reason `GET /api/v1/timelines/direct` was removed too
### Added
- `{POST,PUT} api/v1/lists` now accepts the `exclusive` parameter from Mastodon allowing followed users in the list to be removed from the home timeline
- User profile media now (can) have federated alt text; to this end:
- Mastodon-compatible `avatar_description` and `header_description` parameters are added to account API responses and as input for `PATCH /api/v1/accounts/update_credentials`
- `pleroma.background_image_description` is added to account API responses
- `pleroma_background_image_description` is added as a new parameter to `PATCH /api/v1/accounts/update_credentials`
- `GET /api/v1/statuses/:id` contains the new `poll.akkoma.anonymous` parameter if `poll` is non-null.
It relays if and whether the source instance promised to keep votes anonymous or disclose votes with voter identity.
There are no plans to enable creating non-anonymous polls in Akkoma, but some implementations do.
### Fixed
- fix date-time format in `* /api/v1/markers` to strictly conform to Mastodons ISO 8061 subset
- fix response content-type and styling for the `/embed` endpoint
- do not crash handler when attempting to refresh remote follow stats for users without follow* addresses
- list timelines now include reblogs of users in the list matching Mastodon
- non-federating instances now return a 405 response on inbox `POST`s, matching AP spec
- fixed `GET /api/v1/statuses/:id/context` omitting most local-only posts for authenticated users
- fixed nondeterministic API results in endpoints using GIN indexes; e.g. full-text search
- enforced the host header being present on signatures, and matching our server
### Changed
- our Docker container now sets a default `nofile` `ulimit` to avoid issues on some systems.
Methods to customise this are documented under Configuration - General Optimisation.
- Add reasonable defaults for `:database_config_whitelist`
## 2026.03.1 (3.18.1)
### Update notes
- If you experience degraded performance of database queries after upgrading,
try running `VACUUM ANALYZE;` either manually with `psql` or via the
[database vacuum analyze mix task](https://docs.akkoma.dev/develop/administration/CLI_tasks/database/#analyze).
This will force the planner to pick up index changes if it didnt do so on its own.
### Fixed
- fix WebFinger validation even more.
While actor consent was now properly taken into account after the previous release, some scenarios still allowed bypassing domain consent
- fix posts being federated to us with explicit zero extents crashing the status renderer
- fix pagination parameters being ignored on hashtag timelines
## 2026.03 (3.18.0)
### BREAKING
- Elixir 1.14 is no longer suported, and it's EOL! Upgrade to Elixir 1.15+
- `account` entities in API responses now only contain a cut down version of their servers nodeinfo.
TEMPORARILY a config option is provided to serve the full nodeinfo data again.
HOWEVER this option WILL be removed soon. If you encounter any issues with third-party clients fixed
by using this setting, tell us so we can include all actually needed keys by default.
### REMOVED
### Added
- Mastodon-compatible translation endpoints are now supported too;
the older Akkoma endpoints are deprecated but no immediate plans for removal
- `GET pleroma/conversation/:id/statuses` now supports `with_muted`
- `POST /api/v1/statuses` accepts and now prefers the Mastodon-compatible `quoted_status_id` parameter for quoting a post
- `status` API entities now expose non-shallow quotes in a manner also compatible with Mastodon clients
- support for WebFinger backlinks in ActivityPub actors (FEP-2c59)
### Fixed
- pinning, muting or unmuting a status one is not allowed to access no longer leaks its content
- revoking a favourite on a post one lost access to no longer leaks its content
- user info updates again are actively federated to other servers;
this was accidentally broken in the previous release
- it is no longer possible to reference posts one cannot access when reporting another user
- streamed relationship updates no longer leak follow* counts for users who chose to hide their counts
- WebFinger data and user nicknames no longer allow non-consential associations
- Correctly setup custom WebFinger domains work again
- fix paths of emojis added or updated at runtime and remove emoji from runtime when deleting an entire pack without requiring a full emoji reload
- fix retraction of remote emoji reaction when id is not present or its domain differs from image host
- fix AP ids declared with the canonical type being ignored in XML WebFinger responses
- fix many, many bugs in the conversations API family
- notifications about muted entities are no longer streamed out
- non-UTF-8 usernames no longer lead to internal server errors in API endpoints
- when SimplePolicy rules are configured but the MRF not enabled, its rules no longer interfere with fetching
- fixed remote follow counter refresh on user (re)fetch
- remote users whose follow* counts are private are now actually shown as such in API instead of represeneting them with public zero counters
- fix local follow* collections counting and including AP IDs of deleted users
### Changed
- `PATCH /api/v1/pleroma/conversations/:id` now accepts update parameters via JSON body too
- it is now possible to quote local and ones own private posts provided a compatible scope is used
- on final activity failures the error log now includes the afected activity
- improved performance of `GET api/v1/custom_emoji`
- outgoing HTTP requests now accept compressed responses
- the system CA certificate store is now used by default
- when refreshing remote follow* stats all fetch-related erros are now treated as stats being private;
this avoids spurious error logs and better matches the intent of implementations serving fallback HTML responses on the AP collection endpoints
## 2025.12 (3.17.0)
### REMOVED
- DEPRECATE `/api/v1/timelines/direct`.
Technically this was already deprecated, given we extend mastodon 2.7.2 API
and Mastodon already deprecated it in 2.6.0 before removing it in 3.0.0.
But now we have concrete plans to remove this endpoint in a coming release.
The few remaining useres should switch to the conversations API.
- DEPRECATE `config :pleroma, :instance, skip_thread_containment: false`.
It is due to be removed in one of the next releases if no strong arguments for keeping it are brought up.
It is already semi-broken for large threads and conflicts with pending optimisation and cleanup work.
- support for `exclude_visibilities` in timeline and notification endpoints has been dropped
- support for list visibility / list addressing has been dropped due to lack of usage, maintenance burden and redundancy with the still supported explicit-addressing feature
- support for conversations addressing has been dropped due to lack of usage, maintenance burden and being mostly redundant with explicit addressing
- per-visibility status counters have been dropped from `/api/v1/pleroma/admin/stats`
due to unreasonably perf costs added on most database operations.
For now, the response still contains the fields, but with stubbed-out values.
### Added
- status responses include two new fields for ActivityPub cross-referencing: `akkoma.quote_apid` and `akkoma.in_reply_to_apid`
- attempting to reply to an already deleted post will return an error
(in akkoma-fe the error will be shown and your draft message retained so you can decide
for yourself whether to discard it or copy and repost as a, now intentional, new thread)
- the notification endpoint now supports the `types` parameter for filtering added in vanilla Mastodon
- the mute endpoint now supports the `duration` parameter added in vanilla Mastodon
(fixes temporary mutes created via e.g. Husky)
### Fixed
- replies and quotes to unresolvable posts now fill out IDs for replied to
status, user or quoted status with a 404-ing ID to make them recognisable as
replies/quotes instead of pretending theyre root posts
- querying a status using the ID of a non-post AP activity no longer displays
a duplicate of the post referenced by said activity with mangled author information
- fix users being able to interact (like, emoji react, ...) with posts they cannot access
- fix AP fetches of local non-Create, non-Undo activities exposing the raw, unsanitised content of the referenced object
- the above two combined allowed local users to gain access to private posts
of user they do not follow, but follow a follower of the author.
(remote users and other scenarios were to our knowledge not able to achieve this due to other restrictions)
- fix RSS and Atom feeds of hashtag timelines potentially exposing more information than Mastodon API when restricting unauthenticated API access
- fix mentioning and sending DMs to users with non-ASCII-alphanumerical usernames
- correctly hide and show inlined fallback links for quotes from Mastodon instances
- API requests with multiple unsupported parameters now will ignore all of them up to a certain limit.
If there are too many unsupported parameters this is indicated in the returned error message.
- expose generic type of attachment via Masto API if remote did not send a full MIME type but indicated a generic one
(the \*oma-specific full mime type field in the API response remains generic however, since we don't have this info)
- add back the default banner image we advertise in Masto API
- correctly redirect `/users/:nickname.rss` to the RSS instead of Atom feed
### Changed
- depreacted the `included_types` parameter in the notification endpoint; replaced by `types`
- depreacted the `expires_in` parameter in the mute endpoint; replaced by `duration`
- optimised emoji addition and removal
- emoji reloading now happens asynchronously so you won't run into timeout issues with many emoji and/or a slow disk
- upgraded all of our dependencies; this should reduce issues when running akoma with OTP28
- prefer "summary" over "name" for the attachment alt text of incoming ActivityPub documents;
this fixes alt text federation from GtS and Honk
- slightly improve index overhead for the users table
## 2025.10 (3.16.0)
### REMOVED
- Dropped `accepts_chat_messages` column from users table in database;
it has been unused for almost 3 years
- Healthcheck responses no longer contain job queue data;
it was useless anyway due to lacking any temporal information about failures
and more complete data can be obtained from Prometheus metrics.
### Added
- We mark our MFM posts as FEP-c16b compliant, and retain remote HTML representations for incoming posts marked as FEP-c16b-compliant. (Safety scrubbers are still applied)
@ -27,6 +243,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Added a reference Grafana dashboard and improved documentation for Prometheus metrics
- New mix task `clean_inlined_replies` to delete some unused data from objects
- New mix task `resync_inlined_caches` to retroactively fix various issues with e.g. boosts, emoji reacts and likes
- It is now possible to allow outgoing requests to use HTTP2 via config option,
but due to bugs in the relevant backend this is not the default nor recommended.
- Prometheus metrics now expose count of scheduled and pending jobs per queue
### Fixed
- Internal actors no longer pretend to have unresolvable follow(er|ing) collections
@ -47,6 +266,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- fixed handling of inlined "featured" collections
- fixed user endpoint serving invalid ActivityPub for minimal, authfetch-fallback responses
- remote emoji reacts from IceShrimp.NET instances are now handled consistently and always merged with identical other emoji reactions
- ActivityPub requests signatures are now renewed when following redirects making sure path and host actually match the final URL
- private replies no longer increase the publicly visible reply counter
- unblock activities are no longer federated when block federation is disabled (the default)
- fix like activity database IDs rendering as misattributed posts
### Changed
- Internal and relay actors are now again represented with type "Application"
@ -64,10 +287,13 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- `/api/v1/statuses/:id/reblog` now honours all possible visibilities except `list` and `conversation`
instead of mapping them down to a boolean private/public
- we no longer repeatedly try to deliver to explicitly deleted inboxes
- outgoing requests may now use HTTP2 by default
- Config option `Pleroma.Web.MediaProxy.Invalidation.Http, :options` and
the `:http` subkey of `:media_proxy, :proxy_opts` now only accept
adapter-related settings inside the `:adapter` subkey, no longer on the top-level
- follow requests are now ordered reverse chronologically
## 2025.03
## 2025.03 (3.15.0, 3.15.1, 3.15.2)
### Added
- Oban (worker) dashboard at `/akkoma/oban`
@ -86,11 +312,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- The HTML content for new posts (both Client-to-Server as well as Server-to-Server communication) will now use a different formatting to represent MFM. See [FEP-c16b](https://codeberg.org/fediverse/fep/src/branch/main/fep/c16b/fep-c16b.md) for more details.
- HTTP signatures now test the most likely request-target alias first cutting down on overhead
## 2025.01.01
## 2025.01.01 (3.14.1)
Hotfix: Federation could break if a null value found its way into `should_federate?\1`
## 2025.01
## 2025.01 (3.14.0)
### Added
- New config option `:instance, :cleanup_attachments_delay`
@ -111,7 +337,7 @@ Hotfix: Federation could break if a null value found its way into `should_federa
adopting a proposed AP spec errata and restoring federation
with e.g. IceShrimp.NET and fedify-based implementations
## 3.13.3
## 2024.11 (3.13.3)
### BREAKING
- Minimum PostgreSQL version is raised to 12
@ -134,14 +360,14 @@ Hotfix: Federation could break if a null value found its way into `should_federa
### Changed
- Refactored Rich Media to cache the content in the database. Fetching operations that could block status rendering have been eliminated.
## 2024.04.1 (Security)
## 2024.04.1 (Security) (3.13.2)
### Fixed
- Issue allowing non-owners to use media objects in posts
- Issue allowing use of non-media objects as attachments and crashing timeline rendering
- Issue allowing webfinger spoofing in certain situations
## 2024.04
## 2024.04 (3.13.0, 3.13.1)
### Added
- Support for [FEP-fffd](https://codeberg.org/fediverse/fep/src/branch/main/fep/fffd/fep-fffd.md) (proxy objects)
@ -178,7 +404,7 @@ Hotfix: Federation could break if a null value found its way into `should_federa
- ActivityPub Client-To-Server write API endpoints have been disabled;
read endpoints are planned to be removed next release unless a clear need is demonstrated
## 2024.03
## 2024.03 (3.12.0, 3.12.1, 3.12.3)
### Added
- CLI tasks best-effort checking for past abuse of the recent spoofing exploit
@ -219,7 +445,7 @@ Hotfix: Federation could break if a null value found its way into `should_federa
- Akkoma will refuse to start if this is not set.
- Same with media proxy.
## 2024.02
## 2024.02 (3.11.0)
### Added
- Full compatibility with Erlang OTP26
@ -236,11 +462,15 @@ Hotfix: Federation could break if a null value found its way into `should_federa
### Fixed
- Documentation issue in which a non-existing nginx file was referenced
- Issue where a bad inbox URL could break federation
- Issue where hashtag rel values would be scrubbed
- Issue where short domains listed in `transparency_obfuscate_domains` were not actually obfuscated
## 2023.08
## 2023.08.1 (3.10.4)
### Fixed
- Issue where a bad inbox URL could break federation
## 2023.08 (3.10.0, 3.10.1, 3.10.3)
### Added
@ -284,7 +514,7 @@ Hotfix: Federation could break if a null value found its way into `should_federa
- If you are on oldstable you should NOT attempt to update OTP builds without
first updating your machine.
## 2023.05
## 2023.05 (3.9.0, 3.9.1, 3.9.2, 3.9.3)
### Added
- Custom options for users to accept/reject private messages
@ -301,7 +531,7 @@ Hotfix: Federation could break if a null value found its way into `should_federa
### Security
- Fixed mediaproxy being a bit of a silly billy
## 2023.04
## 2023.04 (3.8.0)
### Added
- Nodeinfo keys for unauthenticated timeline visibility
@ -315,7 +545,7 @@ Hotfix: Federation could break if a null value found its way into `should_federa
use [asdf](https://asdf-vm.com/). At time of writing, elixir 1.14.3 / erlang 25.3
is confirmed to work.
## 2023.03
## 2023.03 (3.7.0, 3.7.1)
### Fixed
- Allowed contentMap to be updated on edit
@ -332,7 +562,7 @@ Hotfix: Federation could break if a null value found its way into `should_federa
- Possibility of using the `style` parameter on `span` elements. This will break certain MFM parameters.
- Option for "default" image description.
## 2023.02
## 2023.02 (3.6.0)
### Added
- Prometheus metrics exporting from `/api/v1/akkoma/metrics`
@ -375,7 +605,7 @@ Hotfix: Federation could break if a null value found its way into `should_federa
- Ensure `config :tesla, :adapter` is either unset, or set to `{Tesla.Adapter.Finch, name: MyFinch}` in your .exs config
- Pleroma-FE will need to be updated to handle the new /api/v1/pleroma endpoints for custom emoji
## 2022.12
## 2022.12 (3.5.0)
### Added
- Config: HTTP timeout options, :pool\_timeout and :receive\_timeout
@ -403,7 +633,7 @@ Hotfix: Federation could break if a null value found its way into `should_federa
### Upgrade Notes
- If you have an old instance, you will probably want to run `mix pleroma.database prune_task` in the foreground to catch it up with the history of your instance.
## 2022.11
## 2022.11 (3.4.0)
### Added
- Officially supported docker release
@ -412,8 +642,6 @@ Hotfix: Federation could break if a null value found its way into `should_federa
- `requested_by` in relationships when the user has requested to follow you
### Changed
- Follows no longer override domain blocks, a domain block is final
- Deletes are now the lowest priority to publish and will be handled after creates
- Domain blocks are now subdomain-matches by default
### Fixed
@ -424,7 +652,14 @@ Hotfix: Federation could break if a null value found its way into `should_federa
to the latest. The changes in OTP24.3 are breaking.
- You can now remove the leading `*.` from domain blocks, but you do not have to.
## 2022.10
## 2022.10.1 (3.3.1)
### Changed
- Follows no longer override domain blocks, a domain block is final
- Deletes are now the lowest priority to publish and will be handled after creates
- Verify that the signature on posts is not domain blocked, and belongs to the correct user
## 2022.10 (3.3.0)
### Added
- Ability to sync frontend profiles between clients, with a name attached
@ -433,14 +668,13 @@ Hotfix: Federation could break if a null value found its way into `should_federa
### Changed
- Emoji updated to latest 15.0 draft
- **Breaking**: `/api/v1/pleroma/backups` endpoints now requires `read:backups` scope instead of `read:accounts`
- Verify that the signature on posts is not domain blocked, and belongs to the correct user
### Fixed
- OAuthPlug no longer joins with the database every call and uses the user cache
- Undo activities no longer try to look up by ID, and render correctly
- prevent false-errors from meilisearch
## 2022.09
## 2022.09 (3.2.0)
### Added
- support for fedibird-fe, and non-breaking API parity for it to function
@ -467,7 +701,7 @@ Hotfix: Federation could break if a null value found its way into `should_federa
### Removed
- Non-finch HTTP adapters. `:tesla, :adapter` is now highly recommended to be set to the default.
## 2022.08
## 2022.08 (3.1.0)
### Added
- extended runtime module support, see config cheatsheet
@ -497,7 +731,7 @@ Hotfix: Federation could break if a null value found its way into `should_federa
- Chats, they were half-baked. Just use PMs.
- Prometheus, it causes massive slowdown
## 2022.07
## 2022.07 (3.0.0)
### Added
- Added move account API

View file

@ -10,6 +10,7 @@
## Supported FEPs
- [FEP-67ff: FEDERATION](https://codeberg.org/fediverse/fep/src/branch/main/fep/67ff/fep-67ff.md)
- [FEP-2c59: Discovery of a Webfinger address from an ActivityPub actor](https://codeberg.org/fediverse/fep/src/branch/main/fep/2c59/fep-2c59.md)
- [FEP-dc88: Formatting Mathematics](https://codeberg.org/fediverse/fep/src/branch/main/fep/dc88/fep-dc88.md)
- [FEP-f1d5: NodeInfo in Fediverse Software](https://codeberg.org/fediverse/fep/src/branch/main/fep/f1d5/fep-f1d5.md)
- [FEP-fffd: Proxy Objects](https://codeberg.org/fediverse/fep/src/branch/main/fep/fffd/fep-fffd.md)
@ -37,6 +38,22 @@ Depending on instance configuration the same may be true for GET requests.
We set the optional extension term `htmlMfm: true` when using content type "text/x.misskeymarkdown".
Incoming messages containing `htmlMfm: true` will not have their content re-parsed.
## WebFinger
Akkoma requires WebFinger implmentations to respond to queries about a given user both when
`acct:user@domain` or the canonical ActivityPub id of the actor is passed as the `resource`.
Akkoma strongly encourages ActivityPub implementations to include
a FEP-2c59-compliant WebFinger backlink in their actor documents.
Without FEP-2c59 and if different domains are used for ActivityPub and the Webfinger subject,
Akkoma relies on either the presence of an host-meta LRDD template on the ActivityPub domain
or a working WebFinger endpoint on the ActivityPub domain. Additionally all WebFinger endpoints
related to the ActivityPub and canonical WebFinger domain SHOULD also respond to queries about
an alternative acct URI constructed with the WebFinger domain passed as the resource.
Without FEP-2c59 Akkoma may not become aware of changes to the
preferred WebFinger `subject` domain for already discovered users.
## Nodeinfo
Akkoma provides many additional entries in its nodeinfo response,

View file

@ -53,7 +53,7 @@ defmodule Pleroma.LoadTesting.Fetcher do
fetch_public_timeline(user, :local)
fetch_public_timeline(user, :tag)
fetch_notifications(user)
fetch_favourites(user)
fetch_favourited_with_fav_id(user)
fetch_long_thread(user)
fetch_timelines_with_reply_filtering(user)
end
@ -378,21 +378,21 @@ defmodule Pleroma.LoadTesting.Fetcher do
end
defp fetch_favourites(user) do
first_page_last = ActivityPub.fetch_favourites(user) |> List.last()
first_page_last = ActivityPub.fetch_favourited_with_fav_id(user) |> List.last()
second_page_last =
ActivityPub.fetch_favourites(user, %{:max_id => first_page_last.id}) |> List.last()
ActivityPub.fetch_favourited_with_fav_id(user, %{:max_id => first_page_last.id}) |> List.last()
third_page_last =
ActivityPub.fetch_favourites(user, %{:max_id => second_page_last.id}) |> List.last()
ActivityPub.fetch_favourited_with_fav_id(user, %{:max_id => second_page_last.id}) |> List.last()
forth_page_last =
ActivityPub.fetch_favourites(user, %{:max_id => third_page_last.id}) |> List.last()
ActivityPub.fetch_favourited_with_fav_id(user, %{:max_id => third_page_last.id}) |> List.last()
Benchee.run(
%{
"Favourites" => fn opts ->
ActivityPub.fetch_favourites(user, opts)
ActivityPub.fetch_favourited_with_fav_id(user, opts)
end
},
inputs: %{
@ -465,7 +465,8 @@ defmodule Pleroma.LoadTesting.Fetcher do
notifications = MastodonAPI.get_notifications(user, opts)
favourites = ActivityPub.fetch_favourites(user)
favourites_keyed = ActivityPub.fetch_favourited_with_fav_id(user)
favourites = Pagiation.unwrap(favourites_keyed)
Benchee.run(
%{

View file

@ -1,3 +1,2 @@
./build.sh 1.14-otp25 1.14.3-erlang-25.3.2-alpine-3.18.0
./build.sh 1.15-otp25 1.15.8-erlang-25.3.2.18-alpine-3.19.7
./build.sh 1.18-otp27 1.18.2-erlang-27.2.4-alpine-3.19.7
./build.sh 1.15-otp25 1.15.8-erlang-25.3.2.18-alpine-3.22.2
./build.sh 1.19-otp28 1.19-erlang-28.0-alpine-3.23.2

View file

@ -27,7 +27,6 @@ config :pleroma, Pleroma.Emails.Mailer, adapter: Swoosh.Adapters.Test, enabled:
config :pleroma, :instance,
email: "admin@example.com",
notify_email: "noreply@example.com",
skip_thread_containment: false,
federating: false,
external_user_synchronization: false
@ -74,8 +73,6 @@ rum_enabled = System.get_env("RUM_ENABLED") == "true"
config :pleroma, :database, rum_enabled: rum_enabled
IO.puts("RUM enabled: #{rum_enabled}")
config :pleroma, Pleroma.ReverseProxy.Client, Pleroma.ReverseProxy.ClientMock
if File.exists?("./config/benchmark.secret.exs") do
import_config "benchmark.secret.exs"
else

View file

@ -51,6 +51,11 @@ config :pleroma, Pleroma.Repo,
queue_target: 20_000,
migration_lock: nil
# password hash strength
config :argon2_elixir,
t_cost: 8,
parallelism: 2
config :pleroma, Pleroma.Captcha,
enabled: true,
seconds_valid: 300,
@ -184,7 +189,7 @@ config :pleroma, :http,
pool_timeout: :timer.seconds(60),
receive_timeout: :timer.seconds(15),
proxy_url: nil,
protocols: [:http2, :http1],
protocols: [:http1],
user_agent: :default,
pool_size: 10,
adapter: []
@ -242,8 +247,8 @@ config :pleroma, :instance,
safe_dm_mentions: false,
healthcheck: false,
remote_post_retention_days: 90,
skip_thread_containment: true,
limit_to_local_content: :unauthenticated,
filter_embedded_nodeinfo: true,
user_bio_length: 5000,
user_name_length: 100,
max_account_fields: 10,
@ -354,16 +359,6 @@ config :pleroma, :assets,
],
default_mascot: :pleroma_fox_tan
config :pleroma, :manifest,
icons: [
%{
src: "/static/logo.svg",
type: "image/svg+xml"
}
],
theme_color: "#282c37",
background_color: "#191b22"
config :pleroma, :activitypub,
unfollow_blocked: true,
outgoing_blocks: false,
@ -560,6 +555,15 @@ config :pleroma, Pleroma.User,
],
email_blacklist: []
config :pleroma, :database_config_whitelist, [
{:pleroma},
{:cors_plug},
{:ex_aws, :s3},
{:mime},
{:web_push_encryption, :vapid_details},
{:logger}
]
config :pleroma, Oban,
repo: Pleroma.Repo,
log: false,
@ -776,7 +780,9 @@ config :pleroma, :frontends,
available: %{
"pleroma-fe" => %{
"name" => "pleroma-fe",
"git" => "https://akkoma.dev/AkkomaGang/pleroma-fe",
"blind_trust" => true,
"git" => "https://akkoma.dev/AkkomaGang/akkoma-fe",
"bugtracker" => "https://akkoma.dev/AkkomaGang/akkoma-fe/issues",
"build_url" =>
"https://akkoma-updates.s3-website.fr-par.scw.cloud/frontend/${ref}/akkoma-fe.zip",
"ref" => "stable",
@ -785,7 +791,9 @@ config :pleroma, :frontends,
# Mastodon-Fe cannot be set as a primary - this is only here so we can update this seperately
"mastodon-fe" => %{
"name" => "mastodon-fe",
"blind_trust" => true,
"git" => "https://akkoma.dev/AkkomaGang/masto-fe",
"bugtracker" => "https://akkoma.dev/AkkomaGang/masto-fe/issues",
"build_url" =>
"https://akkoma-updates.s3-website.fr-par.scw.cloud/frontend/${ref}/masto-fe.zip",
"build_dir" => "distribution",
@ -793,7 +801,9 @@ config :pleroma, :frontends,
},
"fedibird-fe" => %{
"name" => "fedibird-fe",
"blind_trust" => true,
"git" => "https://akkoma.dev/AkkomaGang/fedibird-fe",
"bugtracker" => "https://akkoma.dev/AkkomaGang/fedibird-fe/issues",
"build_url" =>
"https://akkoma-updates.s3-website.fr-par.scw.cloud/frontend/${ref}/fedibird-fe.zip",
"build_dir" => "distribution",
@ -801,7 +811,9 @@ config :pleroma, :frontends,
},
"admin-fe" => %{
"name" => "admin-fe",
"blind_trust" => true,
"git" => "https://akkoma.dev/AkkomaGang/admin-fe",
"bugtracker" => "https://akkoma.dev/AkkomaGang/admin-fe/issues",
"build_url" =>
"https://akkoma-updates.s3-website.fr-par.scw.cloud/frontend/${ref}/admin-fe.zip",
"ref" => "stable"
@ -809,10 +821,31 @@ config :pleroma, :frontends,
# For developers - enables a swagger frontend to view the openapi spec
"swagger-ui" => %{
"name" => "swagger-ui",
"blind_trust" => true,
"git" => "https://github.com/swagger-api/swagger-ui",
# API spec definitions are part of the backend (and the swagger-ui build outdated)
"bugtracker" => "https://akkoma.dev/AkkomaGang/akkoma/issues",
"build_url" => "https://akkoma-updates.s3-website.fr-par.scw.cloud/frontend/swagger-ui.zip",
"build_dir" => "dist",
"ref" => "stable"
},
# Third-party frontends
"pleroma-fe-vanilla" => %{
"name" => "pleroma-fe-vanilla",
"git" => "https://git.pleroma.social/pleroma/pleroma-fe/",
"build_url" =>
"https://git.pleroma.social/api/packages/pleroma/generic/pleroma-fe-builds/${ref}/latest.zip",
"ref" => "develop",
"build_dir" => "dist",
"bugtracker" => "https://git.pleroma.social/pleroma/pleroma-fe/issues"
},
"pl-fe" => %{
"name" => "pl-fe",
"git" => "https://codeberg.org/mkljczk/pl-fe",
"build_url" => "https://pl.mkljczk.pl/pl-fe.zip",
"ref" => "develop",
"build_dir" => ".",
"bugtracker" => "https://codeberg.org/mkljczk/pl-fe/issues"
}
}
@ -826,7 +859,6 @@ config :pleroma, configurable_from_database: false
config :pleroma, Pleroma.Repo,
parameters: [
gin_fuzzy_search_limit: "500",
plan_cache_mode: "force_custom_plan"
]
@ -837,7 +869,8 @@ private_instance? = :if_instance_is_private
config :pleroma, :restrict_unauthenticated,
timelines: %{local: private_instance?, federated: private_instance?, bubble: true},
profiles: %{local: private_instance?, remote: private_instance?},
activities: %{local: private_instance?, remote: private_instance?}
activities: %{local: private_instance?, remote: private_instance?},
search: %{all: private_instance?, resolve: true, paginate: true}
config :pleroma, Pleroma.Web.ApiSpec.CastAndValidate, strict: false
@ -869,9 +902,18 @@ config :pleroma, ConcurrentLimiter, [
{Pleroma.Search, [max_running: 30, max_waiting: 50]}
]
config :pleroma, Pleroma.Web.WebFinger, domain: nil, update_nickname_on_user_fetch: true
config :pleroma, Pleroma.Web.WebFinger,
domain: nil,
# this _forces_ a nickname rediscovery and validation, otherwise only updates when detecting a change
# TODO: default this to false after the fallout from recent WebFinger bugs is healed
update_nickname_on_user_fetch: true
config :pleroma, Pleroma.Search, module: Pleroma.Search.DatabaseSearch
config :pleroma, Pleroma.Search,
module: Pleroma.Search.DatabaseSearch,
# note this + pre- & postprocessing needs to fit into Phoenix/Cowboys timeout too (default: 60s)
task_timeout: 45_000
config :pleroma, Pleroma.Search.DatabaseSearch, gin_fuzzy_search_limit: nil
config :pleroma, Pleroma.Search.Meilisearch,
url: "http://127.0.0.1:7700/",

View file

@ -61,6 +61,18 @@ frontend_options = [
type: :string,
description: "The directory inside the zip file "
},
%{
key: "blind_trust",
label: "Blindly trust frontend devs?",
type: :boolean,
description: "Do NOT change this unless youre really sure"
},
%{
key: "bugtracker",
label: "Bug tracker",
type: :string,
description: "Where to report bugs (for third-party FEs)"
},
%{
key: "custom-http-headers",
label: "Custom HTTP headers",
@ -851,11 +863,6 @@ config :pleroma, :config_description, [
100
]
},
%{
key: :skip_thread_containment,
type: :boolean,
description: "Skip filtering out broken threads. Default: enabled."
},
%{
key: :limit_to_local_content,
type: {:dropdown, :atom},
@ -1489,8 +1496,7 @@ config :pleroma, :config_description, [
group: :pleroma,
key: :manifest,
type: :group,
description:
"This section describe PWA manifest instance-specific values. Currently this option relate only for MastoFE.",
description: "This section describe PWA manifest instance-specific values.",
children: [
%{
key: :icons,
@ -3109,6 +3115,29 @@ config :pleroma, :config_description, [
description: "Disallow viewing remote posts."
}
]
},
%{
key: :search,
type: :map,
description: "Settings for search endpoints.",
children: [
%{
key: :all,
type: :boolean,
description: "Disallow search access entirely."
},
%{
key: :resolve,
type: :boolean,
description: "Disallow fetching not-yet-known remote content via search."
},
%{
key: :paginate,
type: :boolean,
description:
"Disallow traversing past the first page of results (search pagination can be inefficient)."
}
]
}
]
},
@ -3250,8 +3279,7 @@ config :pleroma, :config_description, [
suggestions: [
Pleroma.Web.Preload.Providers.Instance,
Pleroma.Web.Preload.Providers.User,
Pleroma.Web.Preload.Providers.Timelines,
Pleroma.Web.Preload.Providers.StatusNet
Pleroma.Web.Preload.Providers.Timelines
]
}
]
@ -3350,6 +3378,12 @@ config :pleroma, :config_description, [
type: :module,
description: "Selected search module.",
suggestions: {:list_behaviour_implementations, Pleroma.Search.SearchBackend}
},
%{
key: :task_timeout,
type: :integer,
description: "Timeout for individual search tasks.",
suggestions: [45_000]
}
]
},
@ -3484,7 +3518,7 @@ config :pleroma, :config_description, [
key: :module,
type: :module,
description: "Translation module.",
suggestions: {:list_behaviour_implementations, Pleroma.Akkoma.Translator}
suggestions: {:list_behaviour_implementations, Pleroma.Akkoma.Translator.Provider}
}
]
},

View file

@ -35,7 +35,6 @@ config :pleroma, Pleroma.Emails.Mailer, adapter: Swoosh.Adapters.Test, enabled:
config :pleroma, :instance,
email: "admin@example.com",
notify_email: "noreply@example.com",
skip_thread_containment: false,
federating: false,
external_user_synchronization: false,
static_dir: "test/instance_static/"
@ -129,9 +128,7 @@ config :pleroma, :cachex, provider: Pleroma.CachexMock
config :pleroma, Pleroma.Web.WebFinger, update_nickname_on_user_fetch: false
config :pleroma, :side_effects,
ap_streamer: Pleroma.Web.ActivityPub.ActivityPubMock,
logger: Pleroma.LoggerMock
config :pleroma, :side_effects, ap_streamer: Pleroma.Web.ActivityPub.ActivityPubMock
config :pleroma, Pleroma.Search, module: Pleroma.Search.DatabaseSearch

View file

@ -45,6 +45,10 @@ services:
]
volumes:
- .:/opt/akkoma
ulimits:
# Maximum number of open file descriptors. The default should work well for most,
# but can be bumped for very large instances or if seeing FD-related errors.
nofile: 524288
# Copy this into docker-compose.override.yml and uncomment there if you want to use a reverse proxy
#proxy:

View file

@ -205,3 +205,17 @@ Once you have modified the JSON file, you can load it back into the database.
An instance reboot is needed for many changes to take effect,
you may want to visit `/api/v1/pleroma/admin/restart` on your instance
to soft-restart the instance.
## Remove non-whitelisted configs from the database
This removes any configuration value that is not explicitly whitelisted by `:pleroma, :database_config_whitelist`. Might be useful after updating the whitelist.
=== "OTP"
```sh
./bin/pleroma_ctl config filter_whitelisted
```
=== "From Source"
```sh
mix pleroma.config filter_whitelisted
```

View file

@ -120,22 +120,6 @@ when all orphaned activities have been deleted.
- `--no-singles` - Do not delete activites referencing single objects
- `--no-arrays` - Do not delete activites referencing an array of objects
## Create a conversation for all existing DMs
Can be safely re-run
=== "OTP"
```sh
./bin/pleroma_ctl database bump_all_conversations
```
=== "From Source"
```sh
mix pleroma.database bump_all_conversations
```
## Remove duplicated items from following and update followers count for all users
=== "OTP"

View file

@ -19,3 +19,26 @@
- `--delete` - delete local uploads after migrating them to the target uploader
A list of available uploaders can be seen in [Configuration Cheat Sheet](../../configuration/cheatsheet.md#pleromaupload)
## Rewriting old media URLs
After a migration has taken place, old URLs in your database will not have been changed. You
will want to run this task to update these URLs.
Use the full URL here. So if you moved from `media.example.com/media` to `media.another.com/data`, you'd run with arguments
`old_url = https://media.example.com/media` and `new_url = https://media.another.com/data`.
=== "OTP"
```sh
./bin/pleroma_ctl uploads rewrite_media_domain <old_url> <new_url>
```
=== "From Source"
```sh
mix pleroma.uploads rewrite_media_domain <old_url> <new_url>
```
### Options
- `--dry-run` - Do not action any update and simply print what _would_ happen

View file

@ -18,8 +18,8 @@
3. Go to the working directory of Akkoma (default is `/opt/akkoma`)
4. Copy the above-mentioned files back to their original position.
5. Drop the existing database and user[¹]. `sudo -Hu postgres psql -c 'DROP DATABASE akkoma;';` `sudo -Hu postgres psql -c 'DROP USER akkoma;'`
6. Restore the database schema and akkoma role[¹] (replace the password with the one you find in the configuration file), `sudo -Hu postgres psql -c "CREATE USER akkoma WITH ENCRYPTED PASSWORD '<database-password-wich-you-can-find-in-your-configuration-file>';"` `sudo -Hu postgres psql -c "CREATE DATABASE akkoma OWNER akkoma;"`.
7. Now restore the Akkoma instance's data into the empty database schema[¹]: `sudo -Hu postgres pg_restore -d akkoma -v -1 </path/to/backup_location/akkoma.pgdump>`
6. Restore the database schema and akkoma role[¹] (replace the password with the one you find in the configuration file), `sudo -Hu postgres psql -c "CREATE USER akkoma WITH ENCRYPTED PASSWORD '<database-password-wich-you-can-find-in-your-configuration-file>';";` `sudo -Hu postgres psql -c "CREATE DATABASE akkoma OWNER akkoma;"`.
7. Now restore the Akkoma instance's data into the empty database schema[¹]: `sudo -Hu postgres pg_restore -j "$(nproc)" -d akkoma -v </path/to/backup_location/akkoma.pgdump>`
8. If you installed a newer Akkoma version, you should run the database migrations `./bin/pleroma_ctl migrate`[²].
9. Restart the Akkoma service.
10. Run `sudo -Hu postgres vacuumdb --all --analyze-in-stages`. This will quickly generate the statistics so that postgres can properly plan queries.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 152 KiB

After

Width:  |  Height:  |  Size: 165 KiB

Before After
Before After

View file

@ -53,6 +53,10 @@ curl -i -H 'Authorization: Bearer $ACCESS_TOKEN' https://myinstance.example/api/
You may use the eponymous [Prometheus](https://prometheus.io/)
or anything compatible with it like e.g. [VictoriaMetrics](https://victoriametrics.com/).
The latter claims better performance and storage efficiency.
However, at the moment our reference dashboard only works with VictoriaMetrics,
thus if you wish to use use the reference as an easy dropin template you must
use VictoriaMetrics.
Patches to allow the dashboard to work with plain Prometheus are welcome though.
Both of them can usually be easily installed via distro-packages or docker.
Depending on your distro or installation method the preferred way to change the CLI arguments and the location of config files may differ; consult the documentation of your chosen method to find out.
@ -119,7 +123,8 @@ Thats it, youve got a fancy dashboard with long-term, 24/7 metrics now!
Updating the dashboard can be done by just repeating the import process.
Heres an example taken from a healthy, small instance where
nobody was logged in for about the first half of the displayed time span:
nobody was logged in except for a few minutes
resulting in an (expected) spike in incoming requests:
![Full view of the reference dashboard as it looked at the time of writing](img/grafana_dashboard.webp)
!!! note
@ -236,7 +241,7 @@ If this makes too much noise, consider filtering out telltale delivery failures.
On the opposite side of things, a `http_401` error for example is always worth looking into!
## Built-in Dashboard
## Built-in Dashboard (Phoenix)
Administrators can access a live dashboard under `/phoenix/live_dashboard`
giving an overview of uptime, software versions, database stats and more.
@ -253,18 +258,56 @@ as well as database diagnostics.
BEAM VM stats include detailed memory consumption breakdowns
and a full list of running processes for example.
### Postgres Statements Statistics
The built-in dashboard can list the queries your instances spends the
most accumulative time on giving insight into potential bottlenecks
and what might be worth optimising.
This is the “Outliers” tab in “Ecto Stats”.
However for this to work you first need to enable a PostgreSQL extension
as follows:
Add the following two lines two your `postgresql.conf` (typically placed in your data dir):
```
shared_preload_libraries = 'pg_stat_statements'
pg_stat_statements.track = all
```
Now restart PostgreSQL. Then connect to your akkoma database using `psql` and run:
```sql
CREATE EXTENSION IF NOT EXISTS pg_stat_statements;
```
Execution time statistics will now start to be gathered.
To get a representative sample of your instances workload you should wait a week or at least a day.
These statistics are never reset automatically, but with new Akkoma releases and
changes in the servers your instance federates with the workload will evolve.
Thus its a good idea to reset this occasionally using:
```sql
-- get user oid: SELECT oid FROM pg_roles WHERE rolname = 'akkoma';
-- get db oid: SELECT oid FROM pg_database WHERE datname = 'akkoma';
SELECT pg_stat_statements_reset('<akkoma user oid>'::regclass::oid, '<akkoma database oid>'::regclass::oid);
-- or alternatively, to just reset stats for all users and databases:
-- SELECT pg_stat_statements_reset();
```
## Oban Web
This too requires administrator rights to access and can be found under `/akkoma/oban` if enabled.
The exposed aggregate info is mostly redundant with job statistics already tracked in Prometheus,
but it additionally also:
- shows not-yet executed jobs in the backlog of queues
- shows full argument and meta details for each job
- allows interactively deleting or manually retrying jobs
*(keep this in mind when granting people administrator rights!)*
However, there are two caveats:
1. Just as with the other built-in dashboard, data is not kept around
(although here a **short** backlog actually exists);
when you notice an issue during use and go here to check it likely is already too late.
@ -272,4 +315,4 @@ However, there are two caveats:
by default failed and succeeded jobs will disappear after about a minute.
2. This dashboard comes with some seemingly constant-ish overhead.
For large instances this appears to be negligible, but small instances on weaker hardware might suffer.
Thus this dashboard can be disabled in the [config](../cheatsheet.md#oban-web).
Thus this dashboard can be disabled in the [config](../configuration/cheatsheet.md#oban-web).

View file

@ -31,6 +31,10 @@ su -s "$SHELL" akkoma
# Update frontend(s). See Frontend Configuration doc for more information.
./bin/pleroma_ctl frontend install pleroma-fe --ref stable
# If there were migrations and you now experience degraded db perf
# force the planner to pick up the changes to resolve it:
./bin/pleroma_ctl database vacuum analyze
```
If you selected an alternate flavour on installation,
@ -64,4 +68,8 @@ sudo systemctl start akkoma
# Update akkoma-fe frontend to latest stable. For other Frontends see Frontend Configuration doc for more information.
mix pleroma.frontend install pleroma-fe --ref stable
# If there were migrations and you now experience degraded db perf
# force the planner to pick up the changes to resolve it:
mix pleroma.database vacuum analyze
```

View file

@ -54,7 +54,8 @@ To add configuration to your config file, you can copy it from the base config.
* `remote_post_retention_days`: The default number of days to retain remote posts when pruning the database.
* `user_bio_length`: A user bio maximum length (default: `5000`).
* `user_name_length`: A user name maximum length (default: `100`).
* `skip_thread_containment`: Skip filter out broken threads. The default is `false`.
* `skip_thread_containment`: **DEPRECATED**, DO NOT CHANGE THE DEFAULT!
Skip filter out broken threads. The default is `false`.
* `limit_to_local_content`: Limit unauthenticated users to search for local statutes and users only. Possible values: `:unauthenticated`, `:all` and `false`. The default is `:unauthenticated`.
* `max_account_fields`: The maximum number of custom fields in the user profile (default: `10`).
* `max_remote_account_fields`: The maximum number of custom fields in the remote user profile (default: `20`).
@ -306,8 +307,11 @@ To add your own configuration for akkoma-fe, use it like this:
config :pleroma, :frontend_configurations,
pleroma_fe: %{
theme: "pleroma-dark",
# ... see /priv/static/static/config.json for the available keys.
},
backendCommitUrl: 'https://akkoma.dev/AkkomaGang/akkoma/commit/',
frontendCommitUrl: 'https://akkoma.dev/AkkomaGang/akkoma-fe/commit/',
# ... see all available keys at Akkoma-FEs /static/config.json and
# https://akkoma.dev/AkkomaGang/akkoma-fe/src/commit/develop/src/boot/after_store.js :: setSettings
},
masto_fe: %{
showInstanceSpecificPanel: true
}
@ -367,7 +371,8 @@ Currently, the only option relates to mascots on masto-fe
### :manifest
This section describes PWA manifest instance-specific values. Currently this option relate only for masto-fe.
This section describes PWA manifest instance-specific values.
Almost all PWA manifest keys can be overriden or added here, below some particularly relevant examples:
* `icons`: Describe the icons of the app, this is a list of maps describing icons as
detailed in its [spec](https://www.w3.org/TR/appmanifest/#imageresource-and-its-members).
@ -854,25 +859,6 @@ config :logger, :ex_syslogger,
format: "$metadata[$level] $message"
```
## Database options
### RUM indexing for full text search
!!! warning
It is recommended to use PostgreSQL v11 or newer. We have seen some minor issues with lower PostgreSQL versions.
* `rum_enabled`: If RUM indexes should be used. Defaults to `false`.
RUM indexes are an alternative indexing scheme that is not included in PostgreSQL by default. While they may eventually be mainlined, for now they have to be installed as a PostgreSQL extension from https://github.com/postgrespro/rum.
Their advantage over the standard GIN indexes is that they allow efficient ordering of search results by timestamp, which makes search queries a lot faster on larger servers, by one or two orders of magnitude. They take up around 3 times as much space as GIN indexes.
To enable them, both the `rum_enabled` flag has to be set and the following special migration has to be run:
`mix ecto.migrate --migrations-path priv/repo/optional_migrations/rum_indexing/`
This will probably take a long time.
## Authentication
### :admin_token
@ -1086,8 +1072,9 @@ Boolean, enables/disables in-database configuration. Read [transferring the conf
List of valid configuration sections which are allowed to be configured from the
database. Settings stored in the database before the whitelist is configured are
still applied, so it is suggested to only use the whitelist on instances that
have not migrated the config to the database.
still applied. Consider running the `mix pleroma.config filter_whitelisted` task
after updating the whitelist. Read [Remove non-whitelisted configs from the database](../administration/CLI_tasks/config.md#remove-non-whitelisted-configs-from-the-database)
for more information.
Example:
```elixir

View file

@ -7,72 +7,72 @@ The configuration of Akkoma (and Pleroma) has traditionally been managed with a
1. Run the mix task to migrate to the database.
**Source:**
**Source:**
```
$ mix pleroma.config migrate_to_db
```
```
$ mix pleroma.config migrate_to_db
```
or
or
**OTP:**
**OTP:**
*Note: OTP users need Akkoma to be running for `pleroma_ctl` commands to work*
*Note: OTP users need Akkoma to be running for `pleroma_ctl` commands to work*
```
$ ./bin/pleroma_ctl config migrate_to_db
```
```
$ ./bin/pleroma_ctl config migrate_to_db
```
```
Migrating settings from file: /home/pleroma/config/dev.secret.exs
```
Migrating settings from file: /home/pleroma/config/dev.secret.exs
Settings for key instance migrated.
Settings for group :pleroma migrated.
```
Settings for key instance migrated.
Settings for group :pleroma migrated.
```
2. It is recommended to backup your config file now.
```
cp config/dev.secret.exs config/dev.secret.exs.orig
```
```
cp config/dev.secret.exs config/dev.secret.exs.orig
```
3. Edit your Akkoma config to enable database configuration:
```
config :pleroma, configurable_from_database: true
```
```
config :pleroma, configurable_from_database: true
```
4. ⚠️ **THIS IS NOT REQUIRED** ⚠️
Now you can edit your config file and strip it down to the only settings which are not possible to control in the database. e.g., the Postgres (Repo) and webserver (Endpoint) settings cannot be controlled in the database because the application needs the settings to start up and access the database.
Now you can edit your config file and strip it down to the only settings which are not possible to control in the database. e.g., the Postgres (Repo) and webserver (Endpoint) settings cannot be controlled in the database because the application needs the settings to start up and access the database.
Any settings in the database will override those in the config file, but you may find it less confusing if the setting is only declared in one place.
Any settings in the database will override those in the config file, but you may find it less confusing if the setting is only declared in one place.
A non-exhaustive list of settings that are only possible in the config file include the following:
A non-exhaustive list of settings that are only possible in the config file include the following:
* config :pleroma, Pleroma.Web.Endpoint
* config :pleroma, Pleroma.Repo
* config :pleroma, configurable\_from\_database
* config :pleroma, :database, rum_enabled
* config :pleroma, :connections_pool
* config :pleroma, Pleroma.Web.Endpoint
* config :pleroma, Pleroma.Repo
* config :pleroma, configurable\_from\_database
* config :pleroma, :database, rum_enabled
* config :pleroma, :connections_pool
Here is an example of a server config stripped down after migration:
Here is an example of a server config stripped down after migration:
```
use Mix.Config
```
use Mix.Config
config :pleroma, Pleroma.Web.Endpoint,
url: [host: "cool.pleroma.site", scheme: "https", port: 443]
config :pleroma, Pleroma.Web.Endpoint,
url: [host: "cool.pleroma.site", scheme: "https", port: 443]
config :pleroma, Pleroma.Repo,
adapter: Ecto.Adapters.Postgres,
username: "akkoma",
password: "MySecretPassword",
database: "akkoma_prod",
hostname: "localhost"
config :pleroma, Pleroma.Repo,
adapter: Ecto.Adapters.Postgres,
username: "akkoma",
password: "MySecretPassword",
database: "akkoma_prod",
hostname: "localhost"
config :pleroma, configurable_from_database: true
```
config :pleroma, configurable_from_database: true
```
5. Restart your instance and you can now access the Settings tab in admin-fe.
@ -81,28 +81,28 @@ The configuration of Akkoma (and Pleroma) has traditionally been managed with a
1. Run the mix task to migrate back from the database. You'll receive some debugging output and a few messages informing you of what happened.
**Source:**
**Source:**
```
$ mix pleroma.config migrate_from_db
```
```
$ mix pleroma.config migrate_from_db
```
or
or
**OTP:**
**OTP:**
```
$ ./bin/pleroma_ctl config migrate_from_db
```
```
$ ./bin/pleroma_ctl config migrate_from_db
```
```
10:26:30.593 [debug] QUERY OK source="config" db=9.8ms decode=1.2ms queue=26.0ms idle=0.0ms
SELECT c0."id", c0."key", c0."group", c0."value", c0."inserted_at", c0."updated_at" FROM "config" AS c0 []
```
10:26:30.593 [debug] QUERY OK source="config" db=9.8ms decode=1.2ms queue=26.0ms idle=0.0ms
SELECT c0."id", c0."key", c0."group", c0."value", c0."inserted_at", c0."updated_at" FROM "config" AS c0 []
10:26:30.659 [debug] QUERY OK source="config" db=1.1ms idle=80.7ms
SELECT c0."id", c0."key", c0."group", c0."value", c0."inserted_at", c0."updated_at" FROM "config" AS c0 []
Database configuration settings have been saved to config/dev.exported_from_db.secret.exs
```
10:26:30.659 [debug] QUERY OK source="config" db=1.1ms idle=80.7ms
SELECT c0."id", c0."key", c0."group", c0."value", c0."inserted_at", c0."updated_at" FROM "config" AS c0 []
Database configuration settings have been saved to config/dev.exported_from_db.secret.exs
```
2. Remove `config :pleroma, configurable_from_database: true` from your config. The in-database configuration still exists, but it will not be used. Future migrations will erase the database config before importing your config file again.

View file

@ -46,3 +46,38 @@ might help alleviate the impact.
If this condition does **not** hold though,
setting up such a cache likely only worsens latency and wastes memory.
# Ulimits
Large instances may run into issues with too restrictive OS limits,
such as `nofile` (the maximum number of open file descriptors).
For `nofile` specifically, excessively large values can cause issues too however,
since BEAM will allocate a port management structure whose size is based
on this maximum leading to just as excessive RAM usage.
On many distros the default limits per user can be configured in a file
like `/etc/security/limits.conf` (see `man 5 limits.conf`) or an equivalent.
Remember to change both soft and hard limits, such that the hard limit is
always greater or equal to the soft limit.
You can always lower (but not raise) the limits dynamically for initial testing
using e.g. `ulimit -Sn 65536 && /usr/bin/mix phx.server` *(this changes just the soft limit)*.
Our Docker setup initialises the `nofile` limit to a reasonable default.
If needed it can be changed via the `nofile` key in `ulimits` section
of the `akkoma` service; either in `docker-compose.yml` directly,
using the `docker-compose.override.yml` file or without recompiling the
container image via `docker run --ulimit …`.
# DB Search Fuzziness
If using the default database search with a GIN (not RUM!) index
and specifically search database queries hog up unacceptably
much database time, you may cap this impact in exchange for
worse and unstable search results.
See [Search Configuraton](../search.md).
This should not be necessary on all but the largest or oldest unpruned servers.
Also when evaluating search performance keep in mind the time the API endpoint
takes to respond is not the same as the database time. In particular when
searching for URLs they may be fetched via the network taking orders of magnitudes
longer than any normal database query.

View file

@ -2,48 +2,153 @@
{! administration/CLI_tasks/general_cli_task_info.include !}
## Built-in search
## General
Processing of each search request is capped.
If a search exceeds this limit, the API will return an empty result for the affected category.
Too large values might end up capped by Cowboys HTTP request idle timeout, killing the whole response.
The value can be configured with:
```elixir
config :pleroma, Pleroma.Search, task_timeout: 51_610
```
## Search providers
### Built-in search
To use built-in search that has no external dependencies, set the search module to `Pleroma.Activity`:
> config :pleroma, Pleroma.Search, module: Pleroma.Search.DatabaseSearch
```elixir
config :pleroma, Pleroma.Search, module: Pleroma.Search.DatabaseSearch
```
While it has no external dependencies, it has problems with performance and relevancy.
It has no external dependencies and requires the least amount of disk usage.
However, it is slower than external providers and for performance reasons
is limited to sorting results by recency alone instead of match quality.
Result quality may depend on how well your FTS config (typically a language)
matches the posts on your server.
## Meilisearch
Also keep in mind to make use of PostgreSQLs websearch syntax. Full documentation can be found
[here](https://www.postgresql.org/docs/current/textsearch-controls.html#TEXTSEARCH-PARSING-QUERIES).
Note that it's quite a bit more memory hungry than PostgreSQL (around 4-5G for ~1.2 million
posts while idle and up to 7G while indexing initially). The disk usage for this additional index is also
around 4 gigabytes. Like [RUM](./cheatsheet.md#rum-indexing-for-full-text-search) indexes, it offers considerably
higher performance and ordering by timestamp in a reasonable amount of time.
Additionally, the search results seem to be more accurate.
In short, searching for `apple pie` will match everything containing those two words in any order or place of the text.
`"apple pie"` only matches if both words appear and `pie` immediately follows `apple`.
`apple -orange` matches everything referencing apple(s) but not orange(s).
And `or` acts as an operator to be used for alternatives; for a literal word it must be quoted `"or"`.
Due to high memory usage, it may be best to set it up on a different machine, if running akkoma on a low-resource
#### Change FTS config
By default Akkoma uses the `simple` config which performs almost no normalisation (except casing)
nor removes any stop words, making it rather strict but independent of language.
You can change your config with the `database set_text_search_config` task.
See [docs on CJK search](./howto_search_cjk.md) for advanced examples.
For many languages, Postgres already has a preset built-in you can use as is; e.g.
```sh
...database set_text_search_config english
```
#### Fuzzy search
You may choose to limit the set of posts considered during a FTS search for better performance
in exchange for potentially non-deterministic and less relevant results. See documentation of
`gin_fuzzy_search_limit` in [PostgreSQLs docs](https://www.postgresql.org/docs/17/gin.html#GIN-TIPS).
By default FTS search is exact and considers everything. To enforce a limit only for FTS queries set e.g.:
```elixir
config :pleroma, Pleroma.Search.DatabaseSearch, gin_fuzzy_search_limit: 10_000
```
#### RUM
Instead of a GIN index, the built-in database search can also use a RUM index.
The hope was to improve performance at the cost of higher disk-storage (around 3× more)
by leveraging RUMs capability to include extra data (here timestamps for sorting)
in the index thus avoiding additional heap lookups.
However, since search queries still need to filter results down to respect visibility etc
heap lookups are still needed anyway and in practice this probably doesnt actually help much if at all
while taking up more disk space and needing extra setup work.
You probably dont want to use this and if you already do, consider migrating back to GIN.
##### Enable
!!! warning
It is recommended to use PostgreSQL v11 or newer. We have seen some minor issues with lower PostgreSQL versions.
RUM indexes are a third-party PostgreSQL extension.
First install it via your distro if available or manually from source: https://github.com/postgrespro/rum.
Then change your Akkoma config to set:
```elixir
config :pleroma, :database, rum_enabled: true
```
To enable them, both the `rum_enabled` flag has to be set and the following special migration has to be run:
Finally run the special RUM migrations:
```sh
mix ecto.migrate --migrations-path priv/repo/optional_migrations/rum_indexing/
```
This will probably take a long time.
##### Disable
Just delete the config setting again and revert RUM-specific migrations with
```sh
mix ecto.rollback --all --migrations-path priv/repo/optional_migrations/rum_indexing/
```
Then reapply any potential regular GIN versions of the reverted migrations:
```sh
mix ecto.migrate
```
You can now remove the extension again.
### Meilisearch
Note that it's quite a bit more memory hungry than PostgreSQL (around 4-5GB for ~1.2 million
posts while idle and up to 7GB while indexing initially). It also requires significantly more
disk space (around 4GB for the previous example setup).
This however allows it to process individually queries faster while also sorting results
by match quality ("relevancy") rather than just by recency.
Additionally, the search profits from Meilisearchs typo tolerance etc;
see: [Meilisearchs documentation](https://www.meilisearch.com/docs/capabilities/full_text_search/overview).
Due to high memory usage, it may be best to set it up on a different machine, if running Akkoma on a low-resource
computer, and use private key authentication to secure the remote search instance.
To use [meilisearch](https://www.meilisearch.com/), set the search module to `Pleroma.Search.Meilisearch`:
> config :pleroma, Pleroma.Search, module: Pleroma.Search.Meilisearch
```elixir
config :pleroma, Pleroma.Search, module: Pleroma.Search.Meilisearch
```
You then need to set the address of the meilisearch instance, and optionally the private key for authentication. You might
also want to change the `initial_indexing_chunk_size` to be smaller if your server is not very powerful, but not higher than `100_000`,
because Meilisearch will refuse to process it if it's too big. However, in general you want this to be as big as possible, because Meilisearch
indexes faster when it can process many posts in a single batch.
> config :pleroma, Pleroma.Search.Meilisearch,
> url: "http://127.0.0.1:7700/",
> private_key: "private key",
> search_key: "search key",
> initial_indexing_chunk_size: 100_000
```elixir
config :pleroma, Pleroma.Search.Meilisearch,
url: "http://127.0.0.1:7700/",
private_key: "private key",
search_key: "search key",
initial_indexing_chunk_size: 100_000
```
Information about setting up Meilisearch can be found in the
[official documentation](https://docs.meilisearch.com/learn/getting_started/installation.html).
You probably want to start it with `MEILI_NO_ANALYTICS=true` environment variable to disable analytics.
At least version 0.25.0 is required, but you are strongly adviced to use at least 0.26.0, as it introduces
At least version 0.25.0 is required, but you are strongly advised to use at least 0.26.0, as it introduces
the `--enable-auto-batching` option which drastically improves performance. Without this option, the search
is hardly usable on a somewhat big instance.
### Private key authentication (optional)
#### Private key authentication (optional)
To set the private key, use the `MEILI_MASTER_KEY` environment variable when starting. After setting the _master key_,
you have to get the _private key_ and possibly _search key_, which are actually used for authentication.
@ -64,9 +169,9 @@ your configuration file as `private_key`. You should also see a
If your version of Meilisearch only showed the former,
just leave `search_key` completely unset in Akkoma's config.
### Initial indexing
#### Initial indexing
After setting up the configuration, you'll want to index all of your already existsing posts. Only public posts are indexed. You'll only
After setting up the configuration, you'll want to index all of your already existing posts. Only public posts are indexed. You'll only
have to do it one time, but it might take a while, depending on the amount of posts your instance has seen. This is also a fairly RAM
consuming process for `meilisearch`, and it will take a lot of RAM when running if you have a lot of posts (seems to be around 5G for ~1.2
million posts while idle and up to 7G while indexing initially, but your experience may be different).
@ -107,7 +212,7 @@ of indexing and how many posts have actually been indexed, use the `stats` comma
mix pleroma.search.meilisearch stats
```
### Clearing the index
#### Clearing the index
In case you need to clear the index (for example, to re-index from scratch, if that needs to happen for some reason), you can
use the `clear` command:
@ -127,7 +232,7 @@ there is no need to actually clear the whole index, unless you want **all** of i
that cannot be re-created from the database, it should also generally be a lot smaller than the size of your database. Still, the size
depends on the amount of text in posts.
## Elasticsearch
### Elasticsearch
**Note: This requires at least ElasticSearch 7**
@ -135,18 +240,22 @@ As with Meilisearch, this can be rather memory-hungry, but it is very good at wh
To use [Elasticsearch](https://www.elastic.co/), set the search module to `Pleroma.Search.Elasticsearch`:
> config :pleroma, Pleroma.Search, module: Pleroma.Search.Elasticsearch
```elixir
config :pleroma, Pleroma.Search, module: Pleroma.Search.Elasticsearch
```
You then need to set the URL and authentication credentials if relevant.
> config :pleroma, Pleroma.Search.Elasticsearch.Cluster,
> url: "http://127.0.0.1:9200/",
> username: "elastic",
> password: "changeme",
```elixir
config :pleroma, Pleroma.Search.Elasticsearch.Cluster,
url: "http://127.0.0.1:9200/",
username: "elastic",
password: "changeme"
```
### Initial indexing
#### Initial indexing
After setting up the configuration, you'll want to index all of your already existsing posts. You'll only have to do it one time, but it might take a while, depending on the amount of posts your instance has seen.
After setting up the configuration, you'll want to index all of your already existing posts. You'll only have to do it one time, but it might take a while, depending on the amount of posts your instance has seen.
The sequence of actions is as follows:

View file

@ -34,7 +34,7 @@ Backwards-compatibility for admin API endpoints without version prefixes (`/api/
"count": integer,
"users": [
{
"deactivated": bool,
"is_active": bool,
"id": integer,
"nickname": string,
"roles": {
@ -45,8 +45,8 @@ Backwards-compatibility for admin API endpoints without version prefixes (`/api/
"tags": array,
"avatar": string,
"display_name": string,
"confirmation_pending": bool,
"approval_pending": bool,
"is_confirmed": bool,
"is_approved": bool,
"registration_reason": string,
},
...
@ -433,7 +433,7 @@ Response:
* On success: URL of the unfollowed relay
```json
{"https://example.com/relay"}
"https://example.com/relay"
```
## `POST /api/v1/pleroma/admin/users/invite_token`
@ -1173,20 +1173,23 @@ Loads JSON generated from `config/descriptions.exs`.
- Response:
```json
[
{
"id": 1234,
"data": {
"actor": {
"id": 1,
"nickname": "lain"
{
"items": [
{
"id": 1234,
"data": {
"actor": {
"id": 1,
"nickname": "lain"
},
"action": "relay_follow"
},
"action": "relay_follow"
},
"time": 1502812026, // timestamp
"message": "[2017-08-15 15:47:06] @nick0 followed relay: https://example.org/relay" // log message
}
]
"time": 1502812026, // timestamp
"message": "[2017-08-15 15:47:06] @nick0 followed relay: https://example.org/relay" // log message
}
],
"total": 1
}
```
## `POST /api/v1/pleroma/admin/reload_emoji`
@ -1215,24 +1218,10 @@ Loads JSON generated from `config/descriptions.exs`.
## `GET /api/v1/pleroma/admin/stats`
### Stats
**DEPRECATED; DO NOT USE**!!
- Query Params:
- *optional* `instance`: **string** instance hostname (without protocol) to get stats for
- Example: `https://mypleroma.org/api/v1/pleroma/admin/stats?instance=lain.com`
- Response:
```json
{
"status_visibility": {
"direct": 739,
"private": 9,
"public": 17,
"unlisted": 14
}
}
```
Returned information is only stubbed out.
The endpoint will be removed entirely in an upcoming release.
## `GET /api/v1/pleroma/admin/oauth_app`

View file

@ -14,8 +14,6 @@ by the administrator. It is available under `/api/v1/timelines/bubble`.
Adding the parameter `with_muted=true` to the timeline queries will also return activities by muted (not by blocked!) users.
Adding the parameter `exclude_visibilities` to the timeline queries will exclude the statuses with the given visibilities. The parameter accepts an array of visibility types (`public`, `unlisted`, `private`, `direct`), e.g., `exclude_visibilities[]=direct&exclude_visibilities[]=private`.
Adding the parameter `reply_visibility` to the public, bubble or home timelines queries will filter replies. Possible values: without parameter (default) shows all replies, `following` - replies directed to you or users you follow, `self` - replies directed to you.
Adding the parameter `instance=lain.com` to the public timeline will show only statuses originating from `lain.com` (or any remote instance).
@ -32,7 +30,7 @@ Home, public, hashtag & list timelines further accept:
## Statuses
- `visibility`: has additional possible values `list` and `local` (for local-only statuses)
- `visibility`: has additional possible value `local` (for local-only statuses)
- `emoji_reactions`: additional field since Akkoma 3.2.0; identical to `pleroma/emoji_reactions`
Has these additional fields under the `pleroma` object:
@ -51,6 +49,15 @@ Has these additional fields under the `pleroma` object:
- `parent_visible`: If the parent of this post is visible to the user or not.
- `pinned_at`: a datetime (iso8601) when status was pinned, `null` otherwise.
Furthermore, has the following additional attributes under the `poll.akkoma` object *(if `poll` is non-null)*:
- `anonymous`: relays whether the poll creator promised to process votes anonymously *(`true`)*,
publishes votes with voter identity to some parties or the public *(`false`)*
or did not indicate how votes are processed (`null`).
Note this assumes any remotes claim about its process are true and
even if this is truthfully set to `true`, while no regular users ought to have access to voter identities,
the server operator of the polls home instance may in principle still be able to
extract voter identites via the database or side channels.
The `GET /api/v1/statuses/:id/source` endpoint additionally has the following attributes:
- `content_type`: The content type of the status source.
@ -60,6 +67,7 @@ The `GET /api/v1/statuses/:id/source` endpoint additionally has the following at
Has these additional fields in `params`:
- `expires_in`: the number of seconds the posted activity should expire in.
**Deprecated**; replaced by Mastodon-compatible `duration`
## Media Attachments
@ -90,7 +98,6 @@ The `id` parameter can also be the `nickname` of the user. This only works in th
- `with_muted`: include statuses/reactions from muted accounts
- `exclude_reblogs`: exclude reblogs
- `exclude_replies`: exclude replies
- `exclude_visibilities`: exclude visibilities
Endpoints which accept `with_relationships` parameter:
@ -191,8 +198,8 @@ The `type` value is `pleroma:report`
Accepts additional parameters:
- `exclude_visibilities`: will exclude the notifications for activities with the given visibilities. The parameter accepts an array of visibility types (`public`, `unlisted`, `private`, `direct`). Usage example: `GET /api/v1/notifications?exclude_visibilities[]=direct&exclude_visibilities[]=private`.
- `include_types`: will include the notifications for activities with the given types. The parameter accepts an array of types (`mention`, `follow`, `reblog`, `favourite`, `move`, `pleroma:emoji_reaction`, `pleroma:report`). Usage example: `GET /api/v1/notifications?include_types[]=mention&include_types[]=reblog`.
**Deprecated:** replaced by `types` which is equivalent but (by now) also supported by vanilla Mastodon.
## DELETE `/api/v1/notifications/destroy_multiple`
@ -214,8 +221,8 @@ Additional parameters can be added to the JSON body/Form data:
- `content_type`: string, contain the MIME type of the status, it is transformed into HTML by the backend. You can get the list of the supported MIME types with the nodeinfo endpoint.
- `to`: A list of nicknames (like `admin@otp.akkoma.dev` or `admin` on the local server) that will be used to determine who is going to be addressed by this post. Using this will disable the implicit addressing by mentioned names in the `status` body, only the people in the `to` list will be addressed. The normal rules for post visibility are not affected by this and will still apply.
- `visibility`: string, besides standard MastoAPI values (`direct`, `private`, `unlisted`, `local` or `public`) it can be used to address a List by setting it to `list:LIST_ID`.
- `expires_in`: The number of seconds the posted activity should expire in. When a posted activity expires it will be deleted from the server, and a delete request for it will be federated. This needs to be longer than an hour.
- `in_reply_to_conversation_id`: Will reply to a given conversation, addressing only the people who are part of the recipient set of that conversation. Sets the visibility to `direct`.
- `expires_in`: **Deprecated**; replaced by `duration`.
The number of seconds the posted activity should expire in. When a posted activity expires it will be deleted from the server, and a delete request for it will be federated. This needs to be longer than an hour.
## GET `/api/v1/statuses`
@ -253,6 +260,7 @@ Additional parameters can be added to the JSON body/Form data:
- `allow_following_move` - if true, allows automatically follow moved following accounts
- `also_known_as` - array of ActivityPub IDs, needed for following move
- `pleroma_background_image` - sets the background image of the user. Can be set to "" (an empty string) to reset.
- `pleroma_background_image_description` - sets plaintext alt text for the background image of the user. Can be set to "" (an empty string) to delete.
- `discoverable` - if true, external services (search bots) etc. are allowed to index / list the account (regardless of this setting, user will still appear in regular search results).
- `actor_type` - the type of this account.
- `language` - user's preferred language for receiving emails (digest, confirmation, etc.)
@ -361,10 +369,6 @@ The message payload consists of:
- `follower_count`: follower count
- `following_count`: following count
## User muting and thread muting
Both user muting and thread muting can be done for only a certain time by adding an `expires_in` parameter to the API calls and giving the expiration time in seconds.
## Not implemented
Akkoma is generally compatible with the Mastodon 2.7.2 API, but some newer features and non-essential features are omitted. These features usually return an HTTP 200 status code, but with an empty response. While they may be added in the future, they are considered low priority.

View file

@ -376,13 +376,8 @@ See [Admin-API](admin_api.md)
Pleroma Conversations have the same general structure that Mastodon Conversations have. The behavior differs in the following ways when using these endpoints:
1. Pleroma Conversations never add or remove recipients, unless explicitly changed by the user.
1. Pleroma Conversations never add or remove recipients (`accounts` key), unless explicitly changed by the user.
2. Pleroma Conversations statuses can be requested by Conversation id.
3. Pleroma Conversations can be replied to.
Conversations have the additional field `recipients` under the `pleroma` key. This holds a list of all the accounts that will receive a message in this conversation.
The status posting endpoint takes an additional parameter, `in_reply_to_conversation_id`, which, when set, will set the visiblity to direct and address only the people who are the recipients of that Conversation.
⚠ Conversation IDs can be found in direct messages with the `pleroma.direct_conversation_id` key, do not confuse it with `pleroma.conversation_id`.

View file

@ -244,7 +244,7 @@ Post using this scope will never federate to other servers
but for the sake of completeness it is listed here.
In addition to the usual scopes *(public, unlisted, followers-only, direct)*
Akkoma supports an “unlisted” post scope. Such posts will not federate to
Akkoma supports a “local” post scope. Such posts will not federate to
other instances and only be shown to logged-in users on the same instance.
It is included into the local timeline.
This may be useful to discuss or announce instance-specific policies and topics.
@ -267,17 +267,33 @@ special meaning to the potential local-scope identifier.
however those are also shown publicly on the local web interface
and are thus visible to non-members.
## List post scope
Messages originally addressed to a custom list will contain
a `listMessage` field with an unresolvable pseudo ActivityPub id.
# Deprecated and Removed Extensions
The following extensions were used in the past but have been dropped.
Documentation is retained here as a reference and since old objects might
still contains related fields.
## List post scope
Messages originally addressed to a custom list will contain
a `listMessage` field with an unresolvable pseudo ActivityPub id.
!!! note
The concept did not work out too well in practice with even remote servers
recognising the `listMessage` extension being unaware of the state of the
list and resulting weird desyncs in thread display and handling between
servers.
As it was it also never found its way in any known clients or frontends.
A more consistent superset of what this was able to actually do
can be achieved without ActivityPub extensions by explicitly addressing
all intended participants without inline mentions.
While true federated and moderated "lists" or "groups"
will need more work and a different approach.
Thus suport for it was removed and it is recommended
to not create any new implementation of it.
## Actor endpoints
The following endpoints used to be present:

View file

@ -8,6 +8,7 @@ command to install.
You **must** run frontend management tasks as the akkoma user,
the same way you downloaded the build or cloned the git repo before.
This includes adding the same `MIX_ENV=…` prefix as used in prior commands if any!
But otherwise, for most installations, the following will suffice:
=== "OTP"

View file

@ -1,8 +1,8 @@
## Required dependencies
* PostgreSQL 12+
* Elixir 1.14.1+ (currently tested up to 1.18)
* Erlang OTP 25+ (currently tested up to OTP27)
* Elixir 1.15+ (currently tested up to 1.19)
* Erlang OTP 25+ (currently tested up to OTP28)
* git
* file / libmagic
* gcc (clang might also work)

File diff suppressed because it is too large Load diff

View file

@ -22,7 +22,7 @@ upstream phoenix {
# listen [::]:80;
#
# location / {
# return 301 https://$server_name$request_uri;
# return 301 https://$host$request_uri;
# }
# }

View file

@ -11,7 +11,7 @@ defmodule Mix.Tasks.Pleroma.Benchmark do
Benchee.run(%{
"search" => fn ->
Pleroma.Activity.search(nil, "cofe")
Pleroma.Search.DatabaseSearch.search(nil, "cofe", resolve: false)
end
})
end

View file

@ -244,6 +244,61 @@ defmodule Mix.Tasks.Pleroma.Config do
end
end
# Removes non-whitelisted configuration sections
def run(["filter_whitelisted" | rest]) do
{options, [], []} =
OptionParser.parse(
rest,
strict: [force: :boolean],
aliases: [f: :force]
)
force = Keyword.get(options, :force, false)
start_pleroma()
whitelisted_configs = Pleroma.Config.get(:database_config_whitelist)
if whitelisted_configs in [nil, false] do
shell_info("No unwanted settings in ConfigDB. No changes made.")
else
whitelisted_groups =
whitelisted_configs
|> Enum.filter(fn
{_group} -> true
_ -> false
end)
|> Enum.map(fn {group} -> group end)
whitelisted_keys =
whitelisted_configs
|> Enum.filter(fn
{_group, _key} -> true
_ -> false
end)
filtered =
from(c in ConfigDB)
|> Repo.all()
|> Enum.filter(&not_whitelisted?(&1, whitelisted_groups, whitelisted_keys))
if not Enum.empty?(filtered) do
shell_info("The following settings will be removed from ConfigDB:\n")
Enum.each(filtered, &dump(&1))
if force or shell_prompt("Are you sure you want to continue?", "n") in ~w(Yn Y y) do
filtered_ids = Enum.map(filtered, fn %{id: id} -> id end)
Repo.delete_all(from(c in ConfigDB, where: c.id in ^filtered_ids))
else
shell_error("No changes made.")
end
else
shell_info("No unwanted settings in ConfigDB. No changes made.")
end
end
end
@spec migrate_to_db(Path.t() | nil) :: any()
def migrate_to_db(file_path \\ nil) do
with :ok <- Pleroma.Config.DeprecationWarnings.warn() do
@ -443,4 +498,9 @@ defmodule Mix.Tasks.Pleroma.Config do
Ecto.Adapters.SQL.query!(Repo, "TRUNCATE config;")
Ecto.Adapters.SQL.query!(Repo, "ALTER SEQUENCE config_id_seq RESTART;")
end
defp not_whitelisted?(%{group: group, key: key}, whitelisted_groups, whitelisted_keys) do
not Enum.member?(whitelisted_groups, group) and
not Enum.member?(whitelisted_keys, {group, key})
end
end

View file

@ -3,7 +3,6 @@
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Mix.Tasks.Pleroma.Database do
alias Pleroma.Conversation
alias Pleroma.Maintenance
alias Pleroma.Object
alias Pleroma.Repo
@ -286,17 +285,12 @@ defmodule Mix.Tasks.Pleroma.Database do
end
end
def run(["bump_all_conversations"]) do
start_pleroma()
Conversation.bump_for_all_activities()
end
def run(["update_users_following_followers_counts"]) do
start_pleroma()
Repo.transaction(
fn ->
from(u in User, select: u)
from(u in User, select: u, where: u.local or u.is_active)
|> Repo.stream()
|> Stream.each(&User.update_follower_count/1)
|> Stream.run()
@ -603,7 +597,7 @@ defmodule Mix.Tasks.Pleroma.Database do
Ecto.Adapters.SQL.query!(
Pleroma.Repo,
"CREATE OR REPLACE FUNCTION objects_fts_update() RETURNS trigger AS $$ BEGIN
new.fts_content := to_tsvector(new.data->>'content');
new.fts_content := to_tsvector(COALESCE(new.data->>'summary', '') || ' ' || (new.data->>'content'));
RETURN new;
END
$$ LANGUAGE plpgsql",
@ -618,7 +612,14 @@ defmodule Mix.Tasks.Pleroma.Database do
Ecto.Adapters.SQL.query!(
Pleroma.Repo,
"CREATE INDEX CONCURRENTLY objects_fts ON objects USING gin(to_tsvector('#{tsconfig}', data->>'content')); ",
"""
CREATE INDEX CONCURRENTLY objects_fts ON objects USING gin(
to_tsvector(
'#{tsconfig}',
COALESCE(data->>'summary', '') || ' ' || (data->>'content')
)
);
""",
[],
timeout: :infinity
)

View file

@ -44,12 +44,11 @@ defmodule Mix.Tasks.Pleroma.Diagnostics do
|> Map.put(:followed_hashtags, followed_hashtags)
|> Map.delete(:local)
list_memberships = Pleroma.List.memberships(user)
recipients = [user.ap_id | User.following(user)]
query =
Pleroma.Web.ActivityPub.ActivityPub.fetch_activities_query(
recipients ++ list_memberships,
recipients,
params
)
|> limit(20)
@ -71,8 +70,6 @@ defmodule Mix.Tasks.Pleroma.Diagnostics do
|> Map.put(:actor_id, user.ap_id)
|> Map.put(:pinned_object_ids, Map.keys(user.pinned_objects))
list_memberships = Pleroma.List.memberships(user)
recipients =
%{
godmode: params[:godmode],
@ -81,7 +78,7 @@ defmodule Mix.Tasks.Pleroma.Diagnostics do
|> Pleroma.Web.ActivityPub.ActivityPub.user_activities_recipients()
query =
(recipients ++ list_memberships)
recipients
|> Pleroma.Web.ActivityPub.ActivityPub.fetch_activities_query(params)
|> limit(20)

View file

@ -33,7 +33,7 @@ defmodule Mix.Tasks.Pleroma.Email do
Pleroma.User.Query.build(%{
local: true,
is_active: true,
deactivated: false,
is_confirmed: false,
invisible: false
})

View file

@ -41,18 +41,7 @@ defmodule Mix.Tasks.Pleroma.NotificationSettings do
end
defp build_query(hide_notification_contents, options) do
query =
from(u in Pleroma.User,
update: [
set: [
notification_settings:
fragment(
"jsonb_set(notification_settings, '{hide_notification_contents}', ?)",
^hide_notification_contents
)
]
]
)
criteria = %{internal: :allowed, local: true}
user_emails =
options
@ -61,11 +50,11 @@ defmodule Mix.Tasks.Pleroma.NotificationSettings do
|> Enum.map(&String.trim(&1))
|> Enum.reject(&(&1 == ""))
query =
criteria =
if length(user_emails) > 0 do
where(query, [u], u.email in ^user_emails)
Map.put(criteria, :email, user_emails)
else
query
criteria
end
user_nicknames =
@ -75,13 +64,23 @@ defmodule Mix.Tasks.Pleroma.NotificationSettings do
|> Enum.map(&String.trim(&1))
|> Enum.reject(&(&1 == ""))
query =
criteria =
if length(user_nicknames) > 0 do
where(query, [u], u.nickname in ^user_nicknames)
Map.put(criteria, :nickname, user_nicknames)
else
query
criteria
end
query
criteria
|> Pleroma.User.Query.build(criteria)
|> update([u],
set: [
notification_settings:
fragment(
"jsonb_set(notification_settings, '{hide_notification_contents}', ?)",
^hide_notification_contents
)
]
)
end
end

View file

@ -1,68 +0,0 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Mix.Tasks.Pleroma.RefreshCounterCache do
@shortdoc "Refreshes counter cache"
use Mix.Task
alias Pleroma.Activity
alias Pleroma.CounterCache
alias Pleroma.Repo
import Ecto.Query
def run([]) do
Mix.Pleroma.start_pleroma()
instances =
Activity
|> distinct([a], true)
|> select([a], fragment("split_part(?, '/', 3)", a.actor))
|> Repo.all()
instances
|> Enum.with_index(1)
|> Enum.each(fn {instance, i} ->
counters = instance_counters(instance)
CounterCache.set(instance, counters)
Mix.Pleroma.shell_info(
"[#{i}/#{length(instances)}] Setting #{instance} counters: #{inspect(counters)}"
)
end)
Mix.Pleroma.shell_info("Done")
end
defp instance_counters(instance) do
counters = %{"public" => 0, "unlisted" => 0, "private" => 0, "direct" => 0}
Activity
|> where([a], fragment("(? ->> 'type'::text) = 'Create'", a.data))
|> where([a], fragment("split_part(?, '/', 3) = ?", a.actor, ^instance))
|> select(
[a],
{fragment(
"activity_visibility(?, ?, ?)",
a.actor,
a.recipients,
a.data
), count(a.id)}
)
|> group_by(
[a],
fragment(
"activity_visibility(?, ?, ?)",
a.actor,
a.recipients,
a.data
)
)
|> Repo.all(timeout: :timer.minutes(30))
|> Enum.reduce(counters, fn {visibility, count}, acc ->
Map.put(acc, visibility, count)
end)
end
end

View file

@ -5,6 +5,7 @@
defmodule Mix.Tasks.Pleroma.Uploads do
use Mix.Task
import Mix.Pleroma
import Ecto.Query
alias Pleroma.Upload
alias Pleroma.Uploaders.Local
require Logger
@ -97,4 +98,106 @@ defmodule Mix.Tasks.Pleroma.Uploads do
shell_info("Done!")
end
@doc """
Rewrite media domains to somewhere new
"""
def run(["rewrite_media_domain", from_url, to_url | args]) do
dry_run = Enum.member?(args, "--dry-run")
start_pleroma()
shell_info("Rewriting media domain from #{from_url} to #{to_url}")
shell_info("Dry run: #{dry_run}")
# actually selecting based on the attachment URL is stupidly difficult due to it being
# stored as a JSONB array in the `data` field... the easier way to do this is just to iterate though
# local posts
from(o in Pleroma.Object)
|> where(
[o],
fragment(
"?->'url'->0->>'href' LIKE ?
OR
?->'attachment'->0->'url'->0->>'href' LIKE ?",
o.data,
^"#{from_url}%",
o.data,
^"#{from_url}%"
)
)
|> Pleroma.Repo.chunk_stream(100, :batches, timeout: :infinity)
|> Stream.each(fn chunk ->
# now we just rewrite it and save it back, ezpz
chunk
|> Enum.each(fn object ->
new_data =
rewrite_url_object(Map.get(object, :id), Map.get(object, :data), from_url, to_url)
if dry_run do
shell_info(
"Dry run: would update object #{object.id} to new media domain (#{inspect(new_data)})"
)
else
Pleroma.Repo.update!(Ecto.Changeset.change(object, data: new_data))
shell_info("Updated object #{object.id} to new media domain")
end
end)
end)
|> Stream.run()
end
defp rewrite_url(id, url, from_url, to_url) do
new_uri = String.replace(url, from_url, to_url)
check = URI.parse(new_uri)
case check do
%URI{scheme: nil, host: nil} ->
raise("Invalid URL after rewriting: #{new_uri} (object ID: #{id})")
_ ->
new_uri
end
end
# The base object - we're looking for this, it has the actual url
defp rewrite_url_object(id, %{"type" => "Link", "href" => href} = link, from_url, to_url) do
Map.put(link, "href", rewrite_url(id, href, from_url, to_url))
end
defp rewrite_url_object(id, %{"type" => type, "url" => urls} = object, from_url, to_url)
when type in ["Document", "Image"] do
# Document and Image contain url field, which will always be an array of links
Map.put(
object,
"url",
Enum.map(
urls,
fn url -> rewrite_url_object(id, url, from_url, to_url) end
)
)
end
defp rewrite_url_object(
id,
%{"type" => _type, "attachment" => attachments} = object,
from_url,
to_url
) do
# Note will contain an attachment field, which is an array of documents
Map.put(
object,
"attachment",
Enum.map(attachments, fn attachment ->
rewrite_url_object(id, attachment, from_url, to_url)
end)
)
end
defp rewrite_url_object(
_id,
object,
_,
_
) do
shell_info(inspect(object))
raise("Unhandled object format!")
end
end

View file

@ -176,7 +176,7 @@ defmodule Mix.Tasks.Pleroma.User do
def run(["deactivate_all_from_instance", instance]) do
start_pleroma()
Pleroma.User.Query.build(%{nickname: "@#{instance}"})
Pleroma.User.Query.build(%{nickname_suffix: "@#{instance}"})
|> Pleroma.Repo.chunk_stream(500, :batches)
|> Stream.each(fn users ->
users
@ -262,7 +262,7 @@ defmodule Mix.Tasks.Pleroma.User do
Pleroma.User.Query.build(%{
external: true,
is_active: true
deactivated: false
})
|> refetch_public_keys()
end
@ -408,7 +408,7 @@ defmodule Mix.Tasks.Pleroma.User do
Pleroma.User.Query.build(%{
local: true,
is_active: true,
deactivated: false,
is_moderator: false,
is_admin: false,
invisible: false
@ -426,7 +426,7 @@ defmodule Mix.Tasks.Pleroma.User do
Pleroma.User.Query.build(%{
local: true,
is_active: true,
deactivated: false,
is_moderator: false,
is_admin: false,
invisible: false

View file

@ -33,10 +33,6 @@ defmodule Pleroma.Activity do
field(:recipients, {:array, :string}, default: [])
field(:thread_muted?, :boolean, virtual: true)
# A field that can be used if you need to join some kind of other
# id to order / paginate this field by
field(:pagination_id, :string, virtual: true)
# This is a fake relation,
# do not use outside of with_preloaded_user_actor/with_joined_user_actor
has_one(:user_actor, User, on_delete: :nothing, foreign_key: :id)
@ -416,8 +412,6 @@ defmodule Pleroma.Activity do
)
end
defdelegate search(user, query, options \\ []), to: Pleroma.Search.DatabaseSearch
def direct_conversation_id(activity, for_user) do
alias Pleroma.Conversation.Participation

View file

@ -59,6 +59,8 @@ defmodule Pleroma.Activity.HTML do
object = Object.normalize(activity, fetch: false)
add_cache_key_for(activity.id, key)
# callback already produces :commit or :ignore tuples
HTML.ensure_scrubbed_html(content, scrubbers, object.data["fake"] || false, callback)
end)
end

View file

@ -1,8 +1,17 @@
defmodule Pleroma.Akkoma.Translator do
@callback translate(String.t(), String.t() | nil, String.t()) ::
{:ok, String.t(), String.t()} | {:error, any()}
@callback languages() ::
{:ok, [%{name: String.t(), code: String.t()}],
[%{name: String.t(), code: String.t()}]}
| {:error, any()}
@cachex Pleroma.Config.get([:cachex, :provider], Cachex)
def languages do
module = Pleroma.Config.get([:translator, :module])
@cachex.fetch!(:translations_cache, "languages:#{module}}", fn _ ->
with {_, true} <- {:enabled, Pleroma.Config.get([:translator, :enabled])},
{:ok, source_languages, dest_languages} <- module.languages() do
{:commit, {:ok, source_languages, dest_languages}}
else
{:enabled, _} -> {:commit, {:enabled, false}}
{:error, err} -> {:ignore, {:error, err}}
end
end)
end
end

View file

@ -1,5 +1,5 @@
defmodule Pleroma.Akkoma.Translators.ArgosTranslate do
@behaviour Pleroma.Akkoma.Translator
@behaviour Pleroma.Akkoma.Translator.Provider
alias Pleroma.Config
@ -23,7 +23,7 @@ defmodule Pleroma.Akkoma.Translators.ArgosTranslate do
end
end
@impl Pleroma.Akkoma.Translator
@impl Pleroma.Akkoma.Translator.Provider
def languages do
with {response, 0} <- safe_languages() do
langs =
@ -83,7 +83,7 @@ defmodule Pleroma.Akkoma.Translators.ArgosTranslate do
defp htmlify_response(string, _), do: string
@impl Pleroma.Akkoma.Translator
@impl Pleroma.Akkoma.Translator.Provider
def translate(string, nil, to_language) do
# Akkoma's Pleroma-fe expects us to detect the source language automatically.
# Argos-translate doesn't have that option (yet?)
@ -106,4 +106,7 @@ defmodule Pleroma.Akkoma.Translators.ArgosTranslate do
{response, _} -> {:error, "ArgosTranslate failed to translate (#{response})"}
end
end
@impl Pleroma.Akkoma.Translator.Provider
def name, do: "Argos Translate"
end

View file

@ -1,5 +1,5 @@
defmodule Pleroma.Akkoma.Translators.DeepL do
@behaviour Pleroma.Akkoma.Translator
@behaviour Pleroma.Akkoma.Translator.Provider
alias Pleroma.HTTP
alias Pleroma.Config
@ -21,7 +21,7 @@ defmodule Pleroma.Akkoma.Translators.DeepL do
Config.get([:deepl, :tier])
end
@impl Pleroma.Akkoma.Translator
@impl Pleroma.Akkoma.Translator.Provider
def languages do
with {:ok, %{status: 200} = source_response} <- do_languages("source"),
{:ok, %{status: 200} = dest_response} <- do_languages("target"),
@ -48,7 +48,7 @@ defmodule Pleroma.Akkoma.Translators.DeepL do
end
end
@impl Pleroma.Akkoma.Translator
@impl Pleroma.Akkoma.Translator.Provider
def translate(string, from_language, to_language) do
with {:ok, %{status: 200} = response} <-
do_request(api_key(), tier(), string, from_language, to_language),
@ -97,4 +97,7 @@ defmodule Pleroma.Akkoma.Translators.DeepL do
]
)
end
@impl Pleroma.Akkoma.Translator.Provider
def name, do: "DeepL"
end

View file

@ -1,5 +1,5 @@
defmodule Pleroma.Akkoma.Translators.LibreTranslate do
@behaviour Pleroma.Akkoma.Translator
@behaviour Pleroma.Akkoma.Translator.Provider
alias Pleroma.Config
alias Pleroma.HTTP
@ -13,7 +13,7 @@ defmodule Pleroma.Akkoma.Translators.LibreTranslate do
Config.get([:libre_translate, :url])
end
@impl Pleroma.Akkoma.Translator
@impl Pleroma.Akkoma.Translator.Provider
def languages do
with {:ok, %{status: 200} = response} <- do_languages(),
{:ok, body} <- Jason.decode(response.body) do
@ -30,7 +30,7 @@ defmodule Pleroma.Akkoma.Translators.LibreTranslate do
end
end
@impl Pleroma.Akkoma.Translator
@impl Pleroma.Akkoma.Translator.Provider
def translate(string, from_language, to_language) do
with {:ok, %{status: 200} = response} <- do_request(string, from_language, to_language),
{:ok, body} <- Jason.decode(response.body) do
@ -79,4 +79,7 @@ defmodule Pleroma.Akkoma.Translators.LibreTranslate do
HTTP.get(to_string(url))
end
@impl Pleroma.Akkoma.Translator.Provider
def name, do: "LibreTranslate"
end

View file

@ -0,0 +1,9 @@
defmodule Pleroma.Akkoma.Translator.Provider do
@callback translate(String.t(), String.t() | nil, String.t()) ::
{:ok, String.t(), String.t()} | {:error, any()}
@callback languages() ::
{:ok, [%{name: String.t(), code: String.t()}],
[%{name: String.t(), code: String.t()}]}
| {:error, any()}
@callback name() :: String.t()
end

View file

@ -68,14 +68,13 @@ defmodule Pleroma.Application do
http_children() ++
[
Pleroma.Stats,
Pleroma.JobQueueMonitor,
{Majic.Pool, [name: Pleroma.MajicPool, pool_size: Config.get([:majic_pool, :size], 2)]},
{Oban, Config.get(Oban)},
Pleroma.Web.Endpoint,
Pleroma.Web.Telemetry
] ++
elasticsearch_children() ++
task_children(@mix_env) ++
task_children() ++
dont_run_in_test(@mix_env)
# See http://elixir-lang.org/docs/stable/elixir/Supervisor.html
@ -145,34 +144,90 @@ defmodule Pleroma.Application do
defp cachex_children do
[
build_cachex("used_captcha", ttl_interval: seconds_valid_interval()),
build_cachex("user", default_ttl: 25_000, ttl_interval: 1000, limit: 2500),
build_cachex("object", default_ttl: 25_000, ttl_interval: 1000, limit: 2500),
build_cachex("rich_media", default_ttl: :timer.minutes(120), limit: 5000),
build_cachex("scrubber", limit: 2500),
build_cachex("scrubber_management", limit: 2500),
build_cachex("idempotency", expiration: idempotency_expiration(), limit: 2500),
build_cachex("web_resp", limit: 2500),
build_cachex("emoji_packs", expiration: emoji_packs_expiration(), limit: 10),
build_cachex("failed_proxy_url", limit: 2500),
build_cachex("banned_urls", default_ttl: :timer.hours(24 * 30), limit: 5_000),
build_cachex("translations", default_ttl: :timer.hours(24 * 30), limit: 2500),
build_cachex("instances", default_ttl: :timer.hours(24), ttl_interval: 1000, limit: 2500),
build_cachex("rel_me", default_ttl: :timer.hours(24 * 30), limit: 300),
build_cachex("host_meta", default_ttl: :timer.minutes(120), limit: 5000),
build_cachex("http_backoff", default_ttl: :timer.hours(24 * 30), limit: 10000)
build_cachex(
"used_captcha",
expiration: expiration(interval: seconds_valid_interval())
),
build_cachex(
"user",
expiration: expiration(default: 3_000, interval: 1_000),
hooks: [cachex_sched_limit(2500)]
),
build_cachex(
"object",
expiration: expiration(default: 3_000, interval: 1_000),
hooks: [cachex_sched_limit(2500)]
),
build_cachex(
"rich_media",
expiration: expiration(default: :timer.hours(2)),
hooks: [cachex_sched_limit(5000)]
),
build_cachex(
"scrubber",
hooks: [cachex_sched_limit(2500)]
),
build_cachex(
"scrubber_management",
hooks: [cachex_sched_limit(2500)]
),
build_cachex(
"idempotency",
expiration: expiration(default: :timer.hours(6), interval: :timer.minutes(1)),
hooks: [cachex_sched_limit(2500, [], frequency: :timer.minutes(1))]
),
build_cachex(
"web_resp",
hooks: [cachex_sched_limit(2500)]
),
build_cachex(
"emoji_packs",
expiration: expiration(default: :timer.minutes(5), interval: :timer.minutes(1)),
hooks: [cachex_sched_limit(10)]
),
build_cachex(
"failed_proxy_url",
hooks: [cachex_sched_limit(2500)]
),
build_cachex(
"banned_urls",
expiration: expiration(default: :timer.hours(24 * 30)),
hooks: [cachex_sched_limit(5_000, [], frequency: :timer.minutes(5))]
),
build_cachex(
"translations",
expiration: expiration(default: :timer.hours(24 * 30)),
hooks: [cachex_sched_limit(2500)]
),
build_cachex(
"instances",
expiration: expiration(default: :timer.hours(24), interval: 1000),
hooks: [cachex_sched_limit(2500)]
),
build_cachex(
"rel_me",
expiration: expiration(default: :timer.hours(24 * 30)),
hooks: [cachex_sched_limit(300, [], frequency: :timer.minutes(1))]
),
build_cachex(
"host_meta",
expiration: expiration(default: :timer.minutes(120)),
hooks: [cachex_sched_limit(5000, [], frequency: :timer.minutes(1))]
),
build_cachex(
"http_backoff",
expiration: expiration(default: :timer.hours(24 * 30)),
hooks: [cachex_sched_limit(10_000, [], frequency: :timer.minutes(5))]
)
]
end
defp emoji_packs_expiration,
do: expiration(default: :timer.seconds(5 * 60), interval: :timer.seconds(60))
defp idempotency_expiration,
do: expiration(default: :timer.seconds(6 * 60 * 60), interval: :timer.seconds(60))
defp seconds_valid_interval,
do: :timer.seconds(Config.get!([Pleroma.Captcha, :seconds_valid]))
defp cachex_sched_limit(limit, prune_opts \\ [], sched_opts \\ []),
do: hook(module: Cachex.Limit.Scheduled, args: {limit, prune_opts, sched_opts})
@spec build_cachex(String.t(), keyword()) :: map()
def build_cachex(type, opts),
do: %{
@ -200,31 +255,29 @@ defmodule Pleroma.Application do
]
end
@spec task_children(atom()) :: [map()]
@spec task_children() :: [map()]
defp task_children() do
always =
[
%{
id: :web_push_init,
start: {Task, :start_link, [&Pleroma.Web.Push.init/0]},
restart: :temporary
}
]
defp task_children(:test) do
[
%{
id: :web_push_init,
start: {Task, :start_link, [&Pleroma.Web.Push.init/0]},
restart: :temporary
}
]
end
defp task_children(_) do
[
%{
id: :web_push_init,
start: {Task, :start_link, [&Pleroma.Web.Push.init/0]},
restart: :temporary
},
%{
id: :internal_fetch_init,
start: {Task, :start_link, [&Pleroma.Web.ActivityPub.InternalFetchActor.init/0]},
restart: :temporary
}
]
if @mix_env == :test do
always
else
[
%{
id: :internal_fetch_init,
start: {Task, :start_link, [&Pleroma.Web.ActivityPub.InternalFetchActor.init/0]},
restart: :temporary
}
| always
]
end
end
@spec elasticsearch_children :: [Pleroma.Search.Elasticsearch.Cluster]

View file

@ -164,13 +164,13 @@ defmodule Pleroma.ApplicationRequirements do
defp check_system_commands!(:ok) do
filter_commands_statuses = [
check_filter(Pleroma.Upload.Filter.Exiftool.StripMetadata, "exiftool"),
check_filter(Pleroma.Upload.Filter.Exiftool.ReadDescription, "exiftool"),
check_filter(Pleroma.Upload.Filter.Mogrify, "mogrify"),
check_filter(Pleroma.Upload.Filter.Mogrifun, "mogrify"),
check_filter(Pleroma.Upload.Filter.AnalyzeMetadata, "mogrify"),
check_filter(Pleroma.Upload.Filter.AnalyzeMetadata, "convert"),
check_filter(Pleroma.Upload.Filter.AnalyzeMetadata, "ffprobe")
check_filter(Pleroma.Upload.Filter.Exiftool.StripMetadata, ["exiftool"]),
check_filter(Pleroma.Upload.Filter.Exiftool.ReadDescription, ["exiftool"]),
check_filter(Pleroma.Upload.Filter.Mogrify, ["mogrify"]),
check_filter(Pleroma.Upload.Filter.Mogrifun, ["mogrify"]),
check_filter(Pleroma.Upload.Filter.AnalyzeMetadata, ["mogrify"]),
check_filter(Pleroma.Upload.Filter.AnalyzeMetadata, ["magick", "convert"]),
check_filter(Pleroma.Upload.Filter.AnalyzeMetadata, ["ffprobe"])
]
preview_proxy_commands_status =
@ -219,13 +219,13 @@ defmodule Pleroma.ApplicationRequirements do
defp check_repo_pool_size!(result), do: result
defp check_filter(filter, command_required) do
defp check_filter(filter, commands_required) do
filters = Config.get([Pleroma.Upload, :filters])
if filter in filters and not Pleroma.Utils.command_available?(command_required) do
if filter in filters and not Enum.any?(commands_required, &Pleroma.Utils.command_available?/1) do
Logger.error(
"#{filter} is specified in list of Pleroma.Upload filters, but the " <>
"#{command_required} command is not found"
"#{filter} is specified in list of Pleroma.Upload filters, but none of the commands in " <>
"[#{Enum.join(commands_required, ", ")}] are found"
)
false

View file

@ -53,13 +53,15 @@ defmodule Pleroma.Bookmark do
end
@spec destroy(FlakeId.Ecto.CompatType.t(), FlakeId.Ecto.CompatType.t()) ::
{:ok, Bookmark.t()} | {:error, Changeset.t()}
:ok | {:error, any()}
def destroy(user_id, activity_id) do
from(b in Bookmark,
where: b.user_id == ^user_id,
where: b.activity_id == ^activity_id
)
|> Repo.one()
|> Repo.delete()
{cnt, _} =
from(b in Bookmark,
where: b.user_id == ^user_id,
where: b.activity_id == ^activity_id
)
|> Repo.delete_all()
if cnt >= 1, do: :ok, else: {:error, :not_found}
end
end

View file

@ -97,7 +97,7 @@ defmodule Pleroma.Captcha do
defp mark_captcha_as_used(token) do
ttl = seconds_valid() |> :timer.seconds()
@cachex.put(:used_captcha_cache, token, true, ttl: ttl)
@cachex.put(:used_captcha_cache, token, true, expire: ttl)
end
defp method, do: Pleroma.Config.get!([__MODULE__, :method])

View file

@ -22,6 +22,41 @@ defmodule Pleroma.Config.DeprecationWarnings do
"\n* `config :pleroma, :instance, :quarantined_instances` is now covered by `:pleroma, :mrf_simple, :reject`"}
]
def check_skip_thread_containment do
# The default in config/config.exs is "true" since 593b8b1e6a8502cca9bf5559b8bec86f172bbecb
# but when the default is retrieved in code the fallback is still "false"
uses_thread_visibility_filtering = Config.get([:instance, :skip_thread_containment]) != nil
if uses_thread_visibility_filtering do
Logger.warning("""
!!!CONFIG ERROR!!!
config :pleroma, :instance, skip_thread_containment: false
was removed after a deprecation window during which no complaints were raised.
You should drop this setting from your config.
""")
:error
else
:ok
end
end
def check_truncated_nodeinfo_in_accounts do
if !Config.get!([:instance, :filter_embedded_nodeinfo]) do
Logger.warning("""
!!!BUG WORKAROUND DETECTED!!!
Your config is explicitly disabling filtering of nodeinfo data embedded in other Masto API responses
config :pleroma, :instance, filter_embedded_nodeinfo: false
This setting will soon be removed. Any usage of it merely serves as a temporary workaround.
Make sure to file a bug telling us which problems you encountered and circumvented by setting this!
https://akkoma.dev/AkkomaGang/akkoma/issues
We cant fix bugs we dont know about.
""")
end
end
def check_exiftool_filter do
filters = Config.get([Pleroma.Upload]) |> Keyword.get(:filters, [])
@ -222,7 +257,8 @@ defmodule Pleroma.Config.DeprecationWarnings do
check_http_adapter(),
check_uploader_base_url_set(),
check_uploader_base_url_is_not_base_domain(),
check_exiftool_filter()
check_exiftool_filter(),
check_skip_thread_containment()
]
|> Enum.reduce(:ok, fn
:ok, :ok -> :ok

View file

@ -7,7 +7,9 @@ defmodule Pleroma.ConfigDB do
import Ecto.Changeset
import Ecto.Query, only: [select: 3, from: 2]
import Pleroma.Web.Gettext
use Gettext,
backend: Pleroma.Web.Gettext
alias __MODULE__
alias Pleroma.Repo

View file

@ -19,7 +19,8 @@ defmodule Pleroma.Constants do
"context_id",
"deleted_activity_id",
"pleroma_internal",
"generator"
"generator",
"voters"
]
)
@ -28,6 +29,10 @@ defmodule Pleroma.Constants do
~w(index.html robots.txt static static-fe finmoji emoji packs sounds images instance embed sw.js sw-pleroma.js favicon.png schemas doc)
)
# XXX: should we start allowing addressing/visibility to change via updates,
# we must also make sure to sync this new data to the Create activity and its recipients field
# as well as adding more safeguards to inlined statuses in Notifications, since access to
# a previously accessible and notified-about post may be lost.
const(status_updatable_fields,
do: [
"source",

View file

@ -15,7 +15,6 @@ defmodule Pleroma.Conversation do
# This is the context ap id.
field(:ap_id, :string)
has_many(:participations, Participation)
has_many(:users, through: [:participations, :user])
timestamps()
end
@ -45,7 +44,11 @@ defmodule Pleroma.Conversation do
participation = Repo.preload(participation, :recipients)
if Enum.empty?(participation.recipients) do
recipients = User.get_all_by_ap_id(activity.recipients)
recipients =
[activity.actor | activity.recipients]
|> Enum.uniq()
|> User.get_all_by_ap_id()
RecipientShip.create(recipients, participation)
end
end
@ -64,15 +67,16 @@ defmodule Pleroma.Conversation do
ap_id when is_binary(ap_id) and byte_size(ap_id) > 0 <- object.data["context"],
{:ok, conversation} <- create_for_ap_id(ap_id) do
users = User.get_users_from_set(activity.recipients, local_only: false)
local_users = Enum.filter(users, & &1.local)
participations =
Enum.map(users, fn user ->
Enum.map(local_users, fn user ->
invisible_conversation = Enum.any?(users, &User.blocks?(user, &1))
opts = Keyword.put(opts, :invisible_conversation, invisible_conversation)
{:ok, participation} =
Participation.create_for_user_and_conversation(user, conversation, opts)
Participation.create_or_bump(user, conversation, activity.id, opts)
maybe_create_recipientships(participation, activity)
participation
@ -87,21 +91,4 @@ defmodule Pleroma.Conversation do
e -> {:error, e}
end
end
@doc """
This is only meant to be run by a mix task. It creates conversations/participations for all direct messages in the database.
"""
def bump_for_all_activities do
stream =
Pleroma.Web.ActivityPub.ActivityPub.fetch_direct_messages_query()
|> Repo.stream()
Repo.transaction(
fn ->
stream
|> Enum.each(fn a -> create_or_bump_for(a, read: true) end)
end,
timeout: :infinity
)
end
end

View file

@ -12,9 +12,12 @@ defmodule Pleroma.Conversation.Participation do
import Ecto.Changeset
import Ecto.Query
@type t() :: %__MODULE__{}
schema "conversation_participations" do
belongs_to(:user, User, type: FlakeId.Ecto.CompatType)
belongs_to(:conversation, Conversation)
field(:last_bump, FlakeId.Ecto.CompatType)
field(:read, :boolean, default: false)
field(:last_activity_id, FlakeId.Ecto.CompatType, virtual: true)
@ -24,24 +27,26 @@ defmodule Pleroma.Conversation.Participation do
timestamps()
end
def creation_cng(struct, params) do
defp creation_cng(struct, params) do
struct
|> cast(params, [:user_id, :conversation_id, :read])
|> validate_required([:user_id, :conversation_id])
|> cast(params, [:user_id, :conversation_id, :last_bump, :read])
|> validate_required([:user_id, :conversation_id, :last_bump])
end
def create_for_user_and_conversation(user, conversation, opts \\ []) do
def create_or_bump(user, conversation, status_id, opts \\ []) do
read = !!opts[:read]
invisible_conversation = !!opts[:invisible_conversation]
update_on_conflict =
if(invisible_conversation, do: [], else: [read: read])
|> Keyword.put(:updated_at, NaiveDateTime.utc_now())
|> Keyword.put(:last_bump, status_id)
%__MODULE__{}
|> creation_cng(%{
user_id: user.id,
conversation_id: conversation.id,
last_bump: status_id,
read: invisible_conversation || read
})
|> Repo.insert(
@ -51,7 +56,7 @@ defmodule Pleroma.Conversation.Participation do
)
end
def read_cng(struct, params) do
defp read_cng(struct, params) do
struct
|> cast(params, [:read])
|> validate_required([:read])
@ -99,43 +104,87 @@ defmodule Pleroma.Conversation.Participation do
{:ok, user, participations}
end
# used for tests
def mark_as_unread(participation) do
participation
|> read_cng(%{read: false})
|> Repo.update()
end
def for_user(user, params \\ %{}) do
def for_user_with_pagination(user, params \\ %{}) do
from(p in __MODULE__,
where: p.user_id == ^user.id,
order_by: [desc: p.updated_at],
preload: [conversation: [:users]]
preload: [:conversation]
)
|> restrict_recipients(user, params)
|> Pleroma.Pagination.fetch_paginated(params)
|> select([p], %{id: p.last_bump, entry: p})
|> Pleroma.Pagination.fetch_paginated(Map.put(params, :pagination_field, :last_bump))
end
def restrict_recipients(query, user, %{recipients: user_ids}) do
def preload_last_activity_id_and_filter(participations) when is_list(participations) do
participations
|> Enum.map(fn p -> load_last_activity_id(p) end)
|> Enum.filter(fn p -> p.last_activity_id end)
end
defp load_last_activity_id(%__MODULE__{} = participation) do
%{
participation
| last_activity_id: last_activity_id(participation)
}
end
@spec last_activity_id(t(), User.t() | nil) :: Flake.t()
def last_activity_id(participation, user \\ nil)
def last_activity_id(
%__MODULE__{conversation: %Conversation{}} = participation,
user
) do
user =
if user && user.id == participation.user_id do
user
else
case participation.user do
%User{} -> participation.user
_ -> User.get_cached_by_id(participation.user_id)
end
end
ActivityPub.fetch_latest_direct_activity_id_for_context(
participation.conversation.ap_id,
user
)
end
def last_activity_id(%__MODULE__{} = participation, user) do
case Repo.preload(participation, :conversation) do
%{conversation: %Conversation{}} = p -> last_activity_id(p, user)
_ -> nil
end
end
defp restrict_recipients(query, user, %{recipients: user_ids}) do
user_binary_ids =
[user.id | user_ids]
|> Enum.uniq()
|> User.binary_id()
conversation_subquery =
__MODULE__
|> group_by([p], p.conversation_id)
recipient_subquery =
RecipientShip
|> group_by([r], r.participation_id)
|> having(
[p],
count(p.user_id) == ^length(user_binary_ids) and
fragment("array_agg(?) @> ?", p.user_id, ^user_binary_ids)
[r],
count(r.user_id) == ^length(user_binary_ids) and
fragment("array_agg(?) @> ?", r.user_id, ^user_binary_ids)
)
|> select([p], %{id: p.conversation_id})
|> select([r], %{pid: r.participation_id})
query
|> join(:inner, [p], c in subquery(conversation_subquery), on: p.conversation_id == c.id)
|> join(:inner, [p], r in subquery(recipient_subquery), on: p.id == r.pid)
end
def restrict_recipients(query, _, _), do: query
defp restrict_recipients(query, _, _), do: query
def for_user_and_conversation(user, conversation) do
from(p in __MODULE__,
@ -145,26 +194,6 @@ defmodule Pleroma.Conversation.Participation do
|> Repo.one()
end
def for_user_with_last_activity_id(user, params \\ %{}) do
for_user(user, params)
|> Enum.map(fn participation ->
activity_id =
ActivityPub.fetch_latest_direct_activity_id_for_context(
participation.conversation.ap_id,
%{
user: user,
blocking_user: user
}
)
%{
participation
| last_activity_id: activity_id
}
end)
|> Enum.reject(&is_nil(&1.last_activity_id))
end
def get(_, _ \\ [])
def get(nil, _), do: nil
@ -213,14 +242,6 @@ defmodule Pleroma.Conversation.Participation do
|> Repo.aggregate(:count, :id)
end
def unread_conversation_count_for_user(user) do
from(p in __MODULE__,
where: p.user_id == ^user.id,
where: not p.read,
select: %{count: count(p.id)}
)
end
def delete(%__MODULE__{} = participation) do
Repo.delete(participation)
end

View file

@ -1,79 +0,0 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.CounterCache do
alias Pleroma.CounterCache
alias Pleroma.Repo
use Ecto.Schema
import Ecto.Changeset
import Ecto.Query
schema "counter_cache" do
field(:instance, :string)
field(:public, :integer)
field(:unlisted, :integer)
field(:private, :integer)
field(:direct, :integer)
end
def changeset(struct, params) do
struct
|> cast(params, [:instance, :public, :unlisted, :private, :direct])
|> validate_required([:instance])
|> unique_constraint(:instance)
end
def get_by_instance(instance) do
CounterCache
|> select([c], %{
"public" => c.public,
"unlisted" => c.unlisted,
"private" => c.private,
"direct" => c.direct
})
|> where([c], c.instance == ^instance)
|> Repo.one()
|> case do
nil -> %{"public" => 0, "unlisted" => 0, "private" => 0, "direct" => 0}
val -> val
end
end
def get_sum do
CounterCache
|> select([c], %{
"public" => type(sum(c.public), :integer),
"unlisted" => type(sum(c.unlisted), :integer),
"private" => type(sum(c.private), :integer),
"direct" => type(sum(c.direct), :integer)
})
|> Repo.one()
end
def set(instance, values) do
params =
Enum.reduce(
["public", "private", "unlisted", "direct"],
%{"instance" => instance},
fn param, acc ->
Map.put_new(acc, param, Map.get(values, param, 0))
end
)
%CounterCache{}
|> changeset(params)
|> Repo.insert(
on_conflict: [
set: [
public: params["public"],
private: params["private"],
unlisted: params["unlisted"],
direct: params["direct"]
]
],
returning: true,
conflict_target: :instance
)
end
end

View file

@ -4,7 +4,7 @@
defmodule Pleroma.Docs.Translator do
require Pleroma.Docs.Translator.Compiler
require Pleroma.Web.Gettext
use Gettext, backend: Pleroma.Web.Gettext
@before_compile Pleroma.Docs.Translator.Compiler
end

View file

@ -7,6 +7,8 @@ defmodule Pleroma.Docs.Translator.Compiler do
@raw_config Pleroma.Config.Loader.read("config/description.exs")
@raw_descriptions @raw_config[:pleroma][:config_description]
require Gettext.Macros
defmacro __before_compile__(_env) do
strings =
__MODULE__.descriptions()
@ -21,7 +23,8 @@ defmodule Pleroma.Docs.Translator.Compiler do
ctxt = msgctxt_for(path, type)
quote do
Pleroma.Web.Gettext.dpgettext_noop(
Gettext.Macros.dpgettext_noop_with_backend(
Pleroma.Web.Gettext,
"config_descriptions",
unquote(ctxt),
unquote(string)

View file

@ -5,12 +5,13 @@
defmodule Pleroma.Emails.UserEmail do
@moduledoc "User emails"
require Pleroma.Web.Gettext
require Pleroma.Web.GettextCompanion
use Gettext, backend: Pleroma.Web.Gettext
use Pleroma.Web, :mailer
alias Pleroma.Config
alias Pleroma.User
alias Pleroma.Web.Gettext
alias Pleroma.Web.GettextCompanion
import Swoosh.Email
import Phoenix.Swoosh, except: [render_body: 3]
@ -29,7 +30,7 @@ defmodule Pleroma.Emails.UserEmail do
@spec welcome(User.t(), map()) :: Swoosh.Email.t()
def welcome(user, opts \\ %{}) do
Gettext.with_locale_or_default user.language do
GettextCompanion.with_locale_or_default user.language do
new()
|> to(recipient(user))
|> from(Map.get(opts, :sender, sender()))
@ -37,7 +38,7 @@ defmodule Pleroma.Emails.UserEmail do
Map.get(
opts,
:subject,
Gettext.dpgettext(
dpgettext(
"static_pages",
"welcome email subject",
"Welcome to %{instance_name}!",
@ -49,7 +50,7 @@ defmodule Pleroma.Emails.UserEmail do
Map.get(
opts,
:html,
Gettext.dpgettext(
dpgettext(
"static_pages",
"welcome email html body",
"Welcome to %{instance_name}!",
@ -61,7 +62,7 @@ defmodule Pleroma.Emails.UserEmail do
Map.get(
opts,
:text,
Gettext.dpgettext(
dpgettext(
"static_pages",
"welcome email text body",
"Welcome to %{instance_name}!",
@ -73,11 +74,11 @@ defmodule Pleroma.Emails.UserEmail do
end
def password_reset_email(user, token) when is_binary(token) do
Gettext.with_locale_or_default user.language do
GettextCompanion.with_locale_or_default user.language do
password_reset_url = url(~p[/api/v1/pleroma/password_reset/#{token}])
html_body =
Gettext.dpgettext(
dpgettext(
"static_pages",
"password reset email body",
"""
@ -93,9 +94,7 @@ defmodule Pleroma.Emails.UserEmail do
new()
|> to(recipient(user))
|> from(sender())
|> subject(
Gettext.dpgettext("static_pages", "password reset email subject", "Password reset")
)
|> subject(dpgettext("static_pages", "password reset email subject", "Password reset"))
|> html_body(html_body)
end
end
@ -106,11 +105,11 @@ defmodule Pleroma.Emails.UserEmail do
to_email,
to_name \\ nil
) do
Gettext.with_locale_or_default user.language do
GettextCompanion.with_locale_or_default user.language do
registration_url = url(~p[/registration/#{user_invite_token.token}])
html_body =
Gettext.dpgettext(
dpgettext(
"static_pages",
"user invitation email body",
"""
@ -127,7 +126,7 @@ defmodule Pleroma.Emails.UserEmail do
|> to(recipient(to_email, to_name))
|> from(sender())
|> subject(
Gettext.dpgettext(
dpgettext(
"static_pages",
"user invitation email subject",
"Invitation to %{instance_name}",
@ -139,11 +138,11 @@ defmodule Pleroma.Emails.UserEmail do
end
def account_confirmation_email(user) do
Gettext.with_locale_or_default user.language do
GettextCompanion.with_locale_or_default user.language do
confirmation_url = url(~p[/api/account/confirm_email/#{user.id}/#{user.confirmation_token}])
html_body =
Gettext.dpgettext(
dpgettext(
"static_pages",
"confirmation email body",
"""
@ -159,7 +158,7 @@ defmodule Pleroma.Emails.UserEmail do
|> to(recipient(user))
|> from(sender())
|> subject(
Gettext.dpgettext(
dpgettext(
"static_pages",
"confirmation email subject",
"%{instance_name} account confirmation",
@ -171,9 +170,9 @@ defmodule Pleroma.Emails.UserEmail do
end
def approval_pending_email(user) do
Gettext.with_locale_or_default user.language do
GettextCompanion.with_locale_or_default user.language do
html_body =
Gettext.dpgettext(
dpgettext(
"static_pages",
"approval pending email body",
"""
@ -187,7 +186,7 @@ defmodule Pleroma.Emails.UserEmail do
|> to(recipient(user))
|> from(sender())
|> subject(
Gettext.dpgettext(
dpgettext(
"static_pages",
"approval pending email subject",
"Your account is awaiting approval"
@ -198,9 +197,9 @@ defmodule Pleroma.Emails.UserEmail do
end
def successful_registration_email(user) do
Gettext.with_locale_or_default user.language do
GettextCompanion.with_locale_or_default user.language do
html_body =
Gettext.dpgettext(
dpgettext(
"static_pages",
"successful registration email body",
"""
@ -216,7 +215,7 @@ defmodule Pleroma.Emails.UserEmail do
|> to(recipient(user))
|> from(sender())
|> subject(
Gettext.dpgettext(
dpgettext(
"static_pages",
"successful registration email subject",
"Account registered on %{instance_name}",
@ -234,7 +233,7 @@ defmodule Pleroma.Emails.UserEmail do
"""
@spec digest_email(User.t()) :: Swoosh.Email.t() | nil
def digest_email(user) do
Gettext.with_locale_or_default user.language do
GettextCompanion.with_locale_or_default user.language do
notifications = Pleroma.Notification.for_user_since(user, user.last_digest_emailed_at)
mentions =
@ -295,7 +294,7 @@ defmodule Pleroma.Emails.UserEmail do
|> to(recipient(user))
|> from(sender())
|> subject(
Gettext.dpgettext(
dpgettext(
"static_pages",
"digest email subject",
"Your digest from %{instance_name}",
@ -336,12 +335,12 @@ defmodule Pleroma.Emails.UserEmail do
def backup_is_ready_email(backup, admin_user_id \\ nil) do
%{user: user} = Pleroma.Repo.preload(backup, :user)
Gettext.with_locale_or_default user.language do
GettextCompanion.with_locale_or_default user.language do
download_url = Pleroma.Web.PleromaAPI.BackupView.download_url(backup)
html_body =
if is_nil(admin_user_id) do
Gettext.dpgettext(
dpgettext(
"static_pages",
"account archive email body - self-requested",
"""
@ -353,7 +352,7 @@ defmodule Pleroma.Emails.UserEmail do
else
admin = Pleroma.Repo.get(User, admin_user_id)
Gettext.dpgettext(
dpgettext(
"static_pages",
"account archive email body - admin requested",
"""
@ -369,7 +368,7 @@ defmodule Pleroma.Emails.UserEmail do
|> to(recipient(user))
|> from(sender())
|> subject(
Gettext.dpgettext(
dpgettext(
"static_pages",
"account archive email subject",
"Your account archive is ready"

View file

@ -16,7 +16,7 @@ defmodule Pleroma.Emoji do
@ets __MODULE__.Ets
@ets_options [
:ordered_set,
:set,
:protected,
:named_table,
{:read_concurrency, true}
@ -25,6 +25,8 @@ defmodule Pleroma.Emoji do
defstruct [:code, :file, :tags, :safe_code, :safe_file]
@type t :: %__MODULE__{}
@doc "Build emoji struct"
def build({code, file, tags}) do
%__MODULE__{
@ -43,14 +45,14 @@ defmodule Pleroma.Emoji do
GenServer.start_link(__MODULE__, [], name: __MODULE__)
end
@doc "Reloads the emojis from disk."
@doc "Reloads the emojis from disk (asynchronous)"
@spec reload() :: :ok
def reload do
GenServer.call(__MODULE__, :reload)
GenServer.cast(__MODULE__, :reload)
end
@doc "Returns the path of the emoji `name`."
@spec get(String.t()) :: String.t() | nil
@doc "Returns the emoji struct of the given `name` if it exists."
@spec get(String.t()) :: t() | nil
def get(name) do
name =
if String.starts_with?(name, ":") do
@ -62,11 +64,23 @@ defmodule Pleroma.Emoji do
end
case :ets.lookup(@ets, name) do
[{_, path}] -> path
[{_, emoji}] -> emoji
_ -> nil
end
end
@doc "Updates or inserts new emoji (asynchronous)"
@spec add_or_update(t()) :: :ok
def add_or_update(%__MODULE__{} = emoji) do
GenServer.cast(__MODULE__, {:add, emoji})
end
@doc "Delete emoji with given shortcode if it exists (asynchronous)"
@spec delete(String.t()) :: :ok
def delete(code) do
GenServer.cast(__MODULE__, {:delete, code})
end
@spec exist?(String.t()) :: boolean()
def exist?(name), do: not is_nil(get(name))
@ -89,10 +103,14 @@ defmodule Pleroma.Emoji do
{:noreply, state}
end
@doc false
def handle_call(:reload, _from, state) do
update_emojis(Loader.load())
{:reply, :ok, state}
def handle_cast({:add, %__MODULE__{} = emoji}, state) do
:ets.insert(@ets, {emoji.code, emoji})
{:noreply, state}
end
def handle_cast({:delete, code}, state) do
:ets.delete(@ets, code)
{:noreply, state}
end
@doc false

View file

@ -49,12 +49,15 @@ defmodule Pleroma.Emoji.Pack do
Path.join(dir, safe_path)
end
defp tags(%__MODULE__{} = pack), do: ["pack:" <> pack.name]
@spec create(String.t()) :: {:ok, t()} | {:error, File.posix()} | {:error, :empty_values}
def create(name) do
with :ok <- validate_not_empty([name]),
dir <- path_join_name_safe(emoji_path(), name),
:ok <- File.mkdir(dir) do
save_pack(%__MODULE__{
name: name,
path: dir,
pack_file: Path.join(dir, "pack.json")
})
@ -90,9 +93,13 @@ defmodule Pleroma.Emoji.Pack do
@spec delete(String.t()) ::
{:ok, [binary()]} | {:error, File.posix(), binary()} | {:error, :empty_values}
def delete(name) do
with :ok <- validate_not_empty([name]),
pack_path <- path_join_name_safe(emoji_path(), name) do
File.rm_rf(pack_path)
with {_, :ok} <- {:empty, validate_not_empty([name])},
{:ok, pack} <- load_pack(name) do
Enum.each(pack.files, fn {shortcode, _} -> Emoji.delete(shortcode) end)
File.rm_rf(pack.path)
else
{:empty, error} -> error
_ -> {:ok, []}
end
end
@ -142,8 +149,6 @@ defmodule Pleroma.Emoji.Pack do
{item, updated_pack}
end)
Emoji.reload()
{:ok, updated_pack}
after
File.rm_rf(tmp_dir)
@ -169,16 +174,14 @@ defmodule Pleroma.Emoji.Pack do
with :ok <- validate_not_empty([shortcode, filename]),
:ok <- validate_emoji_not_exists(shortcode),
{:ok, updated_pack} <- do_add_file(pack, shortcode, filename, file) do
Emoji.reload()
{:ok, updated_pack}
end
end
defp do_add_file(pack, shortcode, filename, file) do
with :ok <- save_file(file, pack, filename) do
pack
|> put_emoji(shortcode, filename)
|> save_pack()
with :ok <- save_file(file, pack, filename),
{:ok, pack} <- put_emoji(pack, shortcode, filename) do
{:ok, pack}
end
end
@ -188,7 +191,7 @@ defmodule Pleroma.Emoji.Pack do
with :ok <- validate_not_empty([shortcode]),
:ok <- remove_file(pack, shortcode),
{:ok, updated_pack} <- pack |> delete_emoji(shortcode) |> save_pack() do
Emoji.reload()
Emoji.delete(shortcode)
{:ok, updated_pack}
end
end
@ -203,9 +206,8 @@ defmodule Pleroma.Emoji.Pack do
{:ok, updated_pack} <-
pack
|> delete_emoji(shortcode)
|> put_emoji(new_shortcode, new_filename)
|> save_pack() do
Emoji.reload()
|> put_emoji(new_shortcode, new_filename) do
if shortcode != new_shortcode, do: Emoji.delete(shortcode)
{:ok, updated_pack}
end
end
@ -455,7 +457,7 @@ defmodule Pleroma.Emoji.Pack do
# if pack.json MD5 changes, the cache is not valid anymore
%{hash: hash, pack_data: result},
# Add a minute to cache time for every file in the pack
ttl: overall_ttl
expire: overall_ttl
)
result
@ -519,7 +521,17 @@ defmodule Pleroma.Emoji.Pack do
defp put_emoji(pack, shortcode, filename) do
files = Map.put(pack.files, shortcode, filename)
%{pack | files: files, files_count: length(Map.keys(files))}
pack = %{pack | files: files, files_count: length(Map.keys(files))}
url_path = path_join_name_safe("/emoji/", pack.name) |> path_join_safe(filename)
with {:ok, pack} <- save_pack(pack) do
{shortcode, url_path, tags(pack)}
|> Emoji.build()
|> Emoji.add_or_update()
{:ok, pack}
end
end
defp delete_emoji(pack, shortcode) do
@ -580,7 +592,7 @@ defmodule Pleroma.Emoji.Pack do
defp http_get(%URI{} = url), do: url |> to_string() |> http_get()
defp http_get(url) do
with {:ok, %{body: body}} <- Pleroma.HTTP.get(url, [], []) do
with {:ok, %{body: body}} <- Pleroma.HTTP.get(url) do
Jason.decode(body)
end
end

View file

@ -193,6 +193,12 @@ defmodule Pleroma.Filter do
end
end
defp escape_for_regex(plain_phrase) do
# Escape all active characters:
# .^$*+?()[{\|
Regex.replace(~r/\.\^\$\*\+\?\(\)\[\{\\\|/, plain_phrase, fn m -> "\\" <> m end)
end
@spec compose_regex(User.t() | [t()], format()) :: String.t() | Regex.t() | nil
def compose_regex(user_or_filters, format \\ :postgres)
@ -207,7 +213,7 @@ defmodule Pleroma.Filter do
def compose_regex([_ | _] = filters, format) do
phrases =
filters
|> Enum.map(& &1.phrase)
|> Enum.map(&escape_for_regex(&1.phrase))
|> Enum.join("|")
case format do

View file

@ -161,7 +161,11 @@ defmodule Pleroma.FollowingRelationship do
|> where([r], r.state == ^:follow_pending)
|> where([r], r.following_id == ^id)
|> where([r, follower: f], f.is_active == true)
|> select([r, follower: f], f)
end
def get_follow_requesting_users_with_request_id(%User{} = user) do
get_follow_requests_query(user)
|> select([r, follower: f], %{id: r.id, entry: f})
end
def following?(%User{id: follower_id}, %User{id: followed_id}) do

View file

@ -3,6 +3,7 @@
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Formatter do
alias PhoenixHTMLHelpers.Tag
alias Pleroma.HTML
alias Pleroma.User
@ -37,10 +38,10 @@ defmodule Pleroma.Formatter do
nickname_text = get_nickname_text(nickname, opts)
:span
|> Phoenix.HTML.Tag.content_tag(
Phoenix.HTML.Tag.content_tag(
|> Tag.content_tag(
Tag.content_tag(
:a,
["@", Phoenix.HTML.Tag.content_tag(:span, nickname_text)],
["@", Tag.content_tag(:span, nickname_text)],
"data-user": id,
class: "u-url mention",
href: user_url,
@ -68,7 +69,7 @@ defmodule Pleroma.Formatter do
url = "#{Pleroma.Web.Endpoint.url()}/tag/#{tag}"
link =
Phoenix.HTML.Tag.content_tag(:a, tag_text,
Tag.content_tag(:a, tag_text,
class: "hashtag",
"data-tag": tag,
href: url,

View file

@ -14,6 +14,8 @@ defmodule Pleroma.Frontend do
"build_dir" => opts[:build_dir]
}
explicit_source = !!(opts[:file] || opts[:build_dir] || opts[:build_url])
frontend_info =
[:frontends, :available, name]
|> Config.get(%{})
@ -28,6 +30,25 @@ defmodule Pleroma.Frontend do
raise "No ref given or configured"
end
if Map.get(frontend_info, "blind_trust", false) !== true do
bugtracker = frontend_info["bugtracker"]
unless bugtracker || explicit_source do
raise "Configured third-party frontend without a bugtracker; refusing install."
end
bugtracker = bugtracker || "the external frontend developers"
Logger.warning("""
!!!!!!!!
You are installing a third-party frontend not vetted by the Akkoma team.
THERE ARE NO GUARANTTES ABOUT SAFETY AND FUNCTIONALITY!
Do NOT report problems to Akkoma, instead
all bugs must be reported to #{bugtracker}
!!!!!!!!
""")
end
dest = Path.join([dir(), name, ref])
label = "#{name} (#{ref})"
@ -69,7 +90,7 @@ defmodule Pleroma.Frontend do
end
end
def unzip(zip, dest) do
defp unzip(zip, dest) do
File.rm_rf!(dest)
File.mkdir_p!(dest)
@ -84,7 +105,7 @@ defmodule Pleroma.Frontend do
url = String.replace(frontend_info["build_url"], "${ref}", frontend_info["ref"])
with {:ok, %{status: 200, body: zip_body}} <-
Pleroma.HTTP.get(url, [], receive_timeout: 120_000) do
Pleroma.HTTP.get(url, [], adapter: [receive_timeout: 120_000]) do
unzip(zip_body, dest)
else
{:error, e} -> {:error, e}

View file

@ -14,7 +14,6 @@ defmodule Pleroma.Healthcheck do
active: 0,
idle: 0,
memory_used: 0,
job_queue_stats: nil,
healthy: true
@type t :: %__MODULE__{
@ -22,7 +21,6 @@ defmodule Pleroma.Healthcheck do
active: non_neg_integer(),
idle: non_neg_integer(),
memory_used: number(),
job_queue_stats: map(),
healthy: boolean()
}
@ -32,7 +30,6 @@ defmodule Pleroma.Healthcheck do
memory_used: Float.round(:recon_alloc.memory(:allocated) / 1024 / 1024, 2)
}
|> assign_db_info()
|> assign_job_queue_stats()
|> check_health()
end
@ -58,11 +55,6 @@ defmodule Pleroma.Healthcheck do
Map.merge(healthcheck, db_info)
end
defp assign_job_queue_stats(healthcheck) do
stats = Pleroma.JobQueueMonitor.stats()
Map.put(healthcheck, :job_queue_stats, stats)
end
@spec check_health(Healthcheck.t()) :: Healthcheck.t()
def check_health(%{pool_size: pool_size, active: active} = check)
when active >= pool_size do

View file

@ -12,8 +12,10 @@ defmodule Pleroma.Helpers.MediaHelper do
require Logger
def missing_dependencies do
Enum.reduce([imagemagick: "convert", ffmpeg: "ffmpeg"], [], fn {sym, executable}, acc ->
if Pleroma.Utils.command_available?(executable) do
Enum.reduce([imagemagick: ["magick", "convert"], ffmpeg: ["ffmpeg"]], [], fn {sym,
executables},
acc ->
if Enum.any?(executables, &Pleroma.Utils.command_available?/1) do
acc
else
[sym | acc]
@ -22,9 +24,10 @@ defmodule Pleroma.Helpers.MediaHelper do
end
def image_resize(url, options) do
with executable when is_binary(executable) <- System.find_executable("convert"),
with executable when is_binary(executable) <-
Enum.find_value(["magick", "convert"], &System.find_executable/1),
{:ok, args} <- prepare_image_resize_args(options),
{:ok, env} <- HTTP.get(url, [], []),
{:ok, env} <- HTTP.get(url),
{:ok, fifo_path} <- mkfifo() do
args = List.flatten([fifo_path, args])
run_fifo(fifo_path, env, executable, args)
@ -73,7 +76,7 @@ defmodule Pleroma.Helpers.MediaHelper do
# Note: video thumbnail is intentionally not resized (always has original dimensions)
def video_framegrab(url) do
with executable when is_binary(executable) <- System.find_executable("ffmpeg"),
{:ok, env} <- HTTP.get(url, [], []),
{:ok, env} <- HTTP.get(url),
{:ok, fifo_path} <- mkfifo(),
args = [
"-y",

View file

@ -7,10 +7,6 @@ defmodule Pleroma.HTTP do
Wrapper for `Tesla.request/2`.
"""
alias Pleroma.HTTP.AdapterHelper
alias Pleroma.HTTP.Request
alias Pleroma.HTTP.RequestBuilder, as: Builder
alias Tesla.Client
alias Tesla.Env
require Logger
@ -18,6 +14,8 @@ defmodule Pleroma.HTTP do
@type t :: __MODULE__
@type method() :: :get | :post | :put | :delete | :head
@mix_env Mix.env()
@doc """
Performs GET request.
@ -59,40 +57,70 @@ defmodule Pleroma.HTTP do
@spec request(method(), Request.url(), String.t(), Request.headers(), keyword()) ::
{:ok, Env.t()} | {:error, any()}
def request(method, url, body, headers, options) when is_binary(url) do
uri = URI.parse(url)
adapter_opts = AdapterHelper.options(options || [])
adapter_opts =
if uri.scheme == :https do
AdapterHelper.maybe_add_cacerts(adapter_opts, :public_key.cacerts_get())
else
adapter_opts
end
options = put_in(options[:adapter], adapter_opts)
params = options[:params] || []
request = build_request(method, headers, options, url, body, params)
client = Tesla.client([Tesla.Middleware.FollowRedirects, Tesla.Middleware.Telemetry])
options = options |> Keyword.delete(:params)
headers = maybe_add_user_agent(headers)
client = build_client(method)
Logger.debug("Outbound: #{method} #{url}")
request(client, request)
Tesla.request(client,
method: method,
url: url,
query: params,
headers: headers,
body: body,
opts: options
)
rescue
e ->
Logger.error("Failed to fetch #{url}: #{Exception.format(:error, e, __STACKTRACE__)}")
{:error, :fetch_error}
end
@spec request(Client.t(), keyword()) :: {:ok, Env.t()} | {:error, any()}
def request(client, request), do: Tesla.request(client, request)
defp build_client(method) do
# Orders of middlewares matters!
# We start construction with the middlewares _last_ to run
# on outgoing requests (and first on incoming responses).
# This allows using more efficient list prepending.
middlewares = [Tesla.Middleware.Telemetry]
defp build_request(method, headers, options, url, body, params) do
Builder.new()
|> Builder.method(method)
|> Builder.headers(headers)
|> Builder.opts(options)
|> Builder.url(url)
|> Builder.add_param(:body, :body, body)
|> Builder.add_param(:query, :query, params)
|> Builder.convert_to_keyword()
# XXX: just like the user-agent header below, our current mocks can't handle extra headers
# and would break if we used the decompression middleware during tests.
# The :test condition can and should be removed once mocks are fixed.
#
# HEAD responses won't contain a body to compress anyway and we sometimes use
# HEAD requests to determine whether a remote resource is within size limits before fetching it.
# If the server would send a compressed response however, Content-Length will be the size of
# the _compressed_ response body skewing results.
middlewares =
if method != :head and @mix_env != :test do
[Tesla.Middleware.DecompressResponse | middlewares]
else
middlewares
end
middlewares = [
Tesla.Middleware.FollowRedirects,
Pleroma.HTTP.Middleware.HTTPSignature | middlewares
]
Tesla.client(middlewares)
end
# XXX: our test mocks are (too) strict about headers and cannot handle user-agent atm
if @mix_env == :test do
defp maybe_add_user_agent(headers) do
with true <- Pleroma.Config.get([:http, :send_user_agent]) do
[{"user-agent", Pleroma.Application.user_agent()} | headers]
else
_ ->
headers
end
end
else
defp maybe_add_user_agent(headers),
do: [{"user-agent", Pleroma.Application.user_agent()} | headers]
end
end

View file

@ -15,12 +15,6 @@ defmodule Pleroma.HTTP.AdapterHelper do
@type proxy :: {Connection.proxy_type(), Connection.host(), pos_integer(), list()}
def maybe_add_cacerts(opts, nil), do: opts
def maybe_add_cacerts(opts, cacerts) do
put_in(opts, [:pools, :default, :conn_opts, :transport_opts, :cacerts], cacerts)
end
@doc """
Merge default connection & adapter options with received ones.
"""
@ -35,13 +29,11 @@ defmodule Pleroma.HTTP.AdapterHelper do
conn_max_idle_time: Config.get!([:http, :receive_timeout]),
protocols: Config.get!([:http, :protocols]),
conn_opts: [
# Do NOT add cacerts here as this will cause issues for plain HTTP connections!
# (when we upgrade our deps to Mint >= 1.6.0 we can also explicitly enable "inet4: true")
transport_opts: [inet6: true],
# up to at least version 0.20.0, Finch leaves server_push enabled by default for HTTP2,
# but will actually raise an exception when receiving such a response. Tell servers we don't want it.
# see: https://github.com/sneako/finch/issues/325
client_settings: [enable_push: false]
transport_opts: [
inet6: true,
inet4: true,
cacerts: :public_key.cacerts_get()
]
]
]
}

View file

@ -94,7 +94,7 @@ defmodule Pleroma.HTTP.Backoff do
log_ratelimit(status, host, timestamp)
ttl = Timex.diff(timestamp, DateTime.utc_now(), :seconds)
# we will cache the host for 5 minutes
@cachex.put(@backoff_cache, host, true, ttl: ttl)
@cachex.put(@backoff_cache, host, true, expire: ttl)
{:error, :ratelimit}
_ ->

View file

@ -0,0 +1,121 @@
# Akkoma: Magically expressive social media
# Copyright © 2025 Akkoma Authors <https://akkoma.dev/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.HTTP.Middleware.HTTPSignature do
alias Pleroma.User.SigningKey
alias Pleroma.Signature
require Logger
@behaviour Tesla.Middleware
@moduledoc """
Adds a HTTP signature and related headers to requests, if a signing key is set in the request env.
If any other middleware can update the target location (e.g. redirects) this MUST be placed after all of them!
(Note: the third argument holds static middleware options from client creation)
"""
@impl true
def call(env, next, _options) do
env = maybe_sign(env)
Tesla.run(env, next)
end
defp maybe_sign(env) do
case Keyword.get(env.opts, :httpsig) do
%{signing_key: %SigningKey{} = key} ->
set_signature_headers(env, key)
_ ->
env
end
end
defp set_signature_headers(env, key) do
Logger.debug("Signing request to: #{env.url}")
{http_headers, signing_headers} = collect_headers_for_signature(env)
signature = Signature.sign(key, signing_headers, has_body: has_body(env))
set_headers(env, [{"signature", signature} | http_headers])
end
defp has_body(%{body: body}) when body in [nil, ""], do: false
defp has_body(_), do: true
defp set_headers(env, []), do: env
defp set_headers(env, [{key, val} | rest]) do
headers = :proplists.delete(key, env.headers)
headers = [{key, val} | headers]
set_headers(%{env | headers: headers}, rest)
end
# Returns tuple.
# First element is headers+values which need to be added to the HTTP request.
# Second element are all headers to be used for signing, including already existing and pseudo headers.
defp collect_headers_for_signature(env) do
{request_target, host} = get_request_target_and_host(env)
date = http_date()
# content-length is always automatically set later on
# since they are needed to establish working connection.
# Similarly host will always be set for HTTP/1, and technically may be omitted for HTTP/2+
# but Tesla doesnt handle it well if we preset it ourselves (and seems to set it even for HTTP/2 anyway)
http_headers = [{"date", date}]
signing_headers = %{
"(request-target)" => request_target,
"host" => host,
"date" => date
}
if has_body(env) do
append_body_headers(env, http_headers, signing_headers)
else
{http_headers, signing_headers}
end
end
defp append_body_headers(env, http_headers, signing_headers) do
content_length = byte_size(env.body)
digest = digest_value(env)
http_headers = [{"digest", digest} | http_headers]
signing_headers =
Map.merge(signing_headers, %{
"digest" => digest,
"content-length" => content_length
})
{http_headers, signing_headers}
end
defp get_request_target_and_host(env) do
uri = URI.parse(env.url)
rt = "#{env.method} #{uri.path}"
host = host_from_uri(uri)
{rt, host}
end
defp digest_value(env) do
# case Tesla.get_header(env, "digest")
encoded_hash = :crypto.hash(:sha256, env.body) |> Base.encode64()
"SHA-256=" <> encoded_hash
end
defp host_from_uri(%URI{port: port, scheme: scheme, host: host}) do
# https://httpwg.org/specs/rfc9110.html#field.host
# https://www.rfc-editor.org/rfc/rfc3986.html#section-3.2.3
if port == URI.default_port(scheme) do
host
else
"#{host}:#{port}"
end
end
defp http_date() do
now = NaiveDateTime.utc_now()
Timex.lformat!(now, "{WDshort}, {0D} {Mshort} {YYYY} {h24}:{m}:{s} GMT", "en")
end
end

View file

@ -1,23 +0,0 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.HTTP.Request do
@moduledoc """
Request struct.
"""
defstruct method: :get, url: "", query: [], headers: [], body: "", opts: []
@type method :: :head | :get | :delete | :trace | :options | :post | :put | :patch
@type url :: String.t()
@type headers :: [{String.t(), String.t()}]
@type t :: %__MODULE__{
method: method(),
url: url(),
query: keyword(),
headers: headers(),
body: String.t(),
opts: keyword()
}
end

View file

@ -1,102 +0,0 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.HTTP.RequestBuilder do
@moduledoc """
Helper functions for building Tesla requests
"""
alias Pleroma.HTTP.Request
alias Tesla.Multipart
@mix_env Mix.env()
@doc """
Creates new request
"""
@spec new(Request.t()) :: Request.t()
def new(%Request{} = request \\ %Request{}), do: request
@doc """
Specify the request method when building a request
"""
@spec method(Request.t(), Request.method()) :: Request.t()
def method(request, m), do: %{request | method: m}
@doc """
Specify the request method when building a request
"""
@spec url(Request.t(), Request.url()) :: Request.t()
def url(request, u), do: %{request | url: u}
@doc """
Add headers to the request
"""
@spec headers(Request.t(), Request.headers()) :: Request.t()
def headers(request, headers) do
headers_list = maybe_add_user_agent(headers, @mix_env)
%{request | headers: headers_list}
end
@doc """
Add custom, per-request middleware or adapter options to the request
"""
@spec opts(Request.t(), keyword()) :: Request.t()
def opts(request, options), do: %{request | opts: options}
@doc """
Add optional parameters to the request
"""
@spec add_param(Request.t(), atom(), atom(), any()) :: Request.t()
def add_param(request, :query, :query, values), do: %{request | query: values}
def add_param(request, :body, :body, value), do: %{request | body: value}
def add_param(request, :body, key, value) do
request
|> Map.put(:body, Multipart.new())
|> Map.update!(
:body,
&Multipart.add_field(
&1,
key,
Jason.encode!(value),
headers: [{"content-type", "application/json"}]
)
)
end
def add_param(request, :file, name, path) do
request
|> Map.put(:body, Multipart.new())
|> Map.update!(:body, &Multipart.add_file(&1, path, name: name))
end
def add_param(request, :form, name, value) do
Map.update(request, :body, %{name => value}, &Map.put(&1, name, value))
end
def add_param(request, location, key, value) do
Map.update(request, location, [{key, value}], &(&1 ++ [{key, value}]))
end
def convert_to_keyword(request) do
request
|> Map.from_struct()
|> Enum.into([])
end
defp maybe_add_user_agent(headers, :test) do
with true <- Pleroma.Config.get([:http, :send_user_agent]) do
[{"user-agent", Pleroma.Application.user_agent()} | headers]
else
_ ->
headers
end
end
defp maybe_add_user_agent(headers, _),
do: [{"user-agent", Pleroma.Application.user_agent()} | headers]
end

View file

@ -10,15 +10,15 @@ defmodule Pleroma.HTTP.Tzdata do
alias Pleroma.HTTP
@impl true
def get(url, headers, options) do
with {:ok, %Tesla.Env{} = env} <- HTTP.get(url, headers, options) do
def get(url, headers, _options) do
with {:ok, %Tesla.Env{} = env} <- HTTP.get(url, headers) do
{:ok, {env.status, env.headers, env.body}}
end
end
@impl true
def head(url, headers, options) do
with {:ok, %Tesla.Env{} = env} <- HTTP.head(url, headers, options) do
def head(url, headers, _options) do
with {:ok, %Tesla.Env{} = env} <- HTTP.head(url, headers) do
{:ok, {env.status, env.headers}}
end
end

View file

@ -5,8 +5,8 @@
defmodule Pleroma.HTTP.WebPush do
@moduledoc false
def post(url, payload, headers, options \\ []) do
def post(url, payload, headers, _options) do
list_headers = Map.to_list(headers)
Pleroma.HTTP.post(url, payload, list_headers, options)
Pleroma.HTTP.post(url, payload, list_headers)
end
end

View file

@ -242,7 +242,7 @@ defmodule Pleroma.Instances.Instance do
{:ok,
Enum.find(links, &(&1["rel"] == "http://nodeinfo.diaspora.software/ns/schema/2.0"))},
{:ok, %Tesla.Env{body: data}} <-
Pleroma.HTTP.get(href, [{"accept", "application/json"}], []),
Pleroma.HTTP.get(href, [{"accept", "application/json"}]),
{:length, true} <- {:length, String.length(data) < 50_000},
{:ok, nodeinfo} <- Jason.decode(data) do
nodeinfo
@ -270,7 +270,7 @@ defmodule Pleroma.Instances.Instance do
with true <- Pleroma.Config.get([:instances_favicons, :enabled]),
{_, true} <- {:reachable, reachable?(instance_uri.host)},
{:ok, %Tesla.Env{body: html}} <-
Pleroma.HTTP.get(to_string(instance_uri), [{"accept", "text/html"}], []),
Pleroma.HTTP.get(to_string(instance_uri), [{"accept", "text/html"}]),
{_, [favicon_rel | _]} when is_binary(favicon_rel) <-
{:parse, html |> Floki.parse_document!() |> Floki.attribute("link[rel=icon]", "href")},
{_, favicon} when is_binary(favicon) <-
@ -299,7 +299,7 @@ defmodule Pleroma.Instances.Instance do
end
def perform(:delete_instance, host) when is_binary(host) do
User.Query.build(%{nickname: "@#{host}"})
User.Query.build(%{nickname_suffix: "@#{host}"})
|> Repo.chunk_stream(100, :batches)
|> Stream.each(fn users ->
users

View file

@ -1,95 +0,0 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.JobQueueMonitor do
use GenServer
@initial_state %{workers: %{}, queues: %{}, processed_jobs: 0}
@queue %{processed_jobs: 0, success: 0, failure: 0}
@operation %{processed_jobs: 0, success: 0, failure: 0}