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
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.
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
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.
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
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.
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).
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.
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.
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.
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
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.
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
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.
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.
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.
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.
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.
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.
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.
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.
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
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.
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>
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
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.
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.
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
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.
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.
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.
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
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)
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.
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.
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
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>
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
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.
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.
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.
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.
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.
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.
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
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.
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>
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
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.
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.
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.
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.
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.
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.
The advertised file no longer exists, probably since
the backend repo stopped carrying a precompiled frontend.
Also highlight options for custom commit URLs.
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
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.
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.
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.
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.
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.
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.
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)
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.
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
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
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.
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.
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.
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.
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.
It makes discovery and validation of the desired webfinger address
much easier. Future commits will actually use it for validation
and nick change discovery.
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.
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
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
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
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
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
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
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.
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.
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.
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
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)
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.
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
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.
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.
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
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.
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.
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.
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.
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.
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
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>
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
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.
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.
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.
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.
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>
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.
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
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.
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.
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.
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.
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.
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
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
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>
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
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.
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.
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.
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
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
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.
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.
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.
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.
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.
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
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
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
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)
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.
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
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.
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
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.
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
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
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.
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.
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.
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
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.
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)
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.
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)
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.
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
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.
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.
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
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
@ -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 one’s 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 the `skipThreadContainment` key from nodeinfo’s `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 Mastodon’s 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
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 they’re 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
@ -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.
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.
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 @@ That’s it, you’ve got a fancy dashboard with long-term, 24/7 metrics now!
Updating the dashboard can be done by just repeating the import process.
Here’s 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:

!!! 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 it’s 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).
@ -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.
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:
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)
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:
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.
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.
@ -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 remote’s 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 poll’s 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.
@ -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.
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`.