Files
Neon Overlord f814c87c79
build / build (push) Failing after 2m6s
ci: build in a Gitea runner, and document the fork end to end
The build recipe existed only as a script in /tmp on a build host, which is
precisely the state this node's rules exist to prevent -- the only copy of
how the artifact is produced living on an ephemeral machine. It is now a
workflow in the repository, so a release is reproducible from the repo alone.

The workflow installs composer from the distribution before running make.
Upstream's `make composer` falls back to piping getcomposer.org/installer
into php when composer is absent; the job now fails loudly instead.

Excludes .gitea from the release archive alongside .github.

README rewritten to carry the whole rationale: the six link sites and what
was wrong with each, the measured coverage of url/uri that justifies the
fallback order, why upstream's local-link behaviour is deliberate rather
than a bug, what was renamed and what was deliberately not, the two build
caveats, and authorship.

Assisted-by: Claude Code:claude-opus-5
2026-08-03 15:09:36 -07:00

202 lines
7.7 KiB
Markdown

# GoToSocial integration into Nextcloud
🦥 Put a GoToSocial in your cloud!
Dashboard widgets for your GoToSocial notifications and home timeline, plus
unified search over posts, people and hashtags, and a "share to GoToSocial"
action on public share links.
A fork of [`nextcloud/integration_mastodon`](https://github.com/nextcloud/integration_mastodon)
by Julien Veyssier, adapted for GoToSocial servers. AGPL-3.0, same as upstream.
---
## Why this fork exists
GoToSocial implements the Mastodon client API, so Mastodon clients mostly work
against it — but one assumption doesn't hold.
Mastodon serves a web page for *every* status it knows about, including ones
that arrived over federation. GoToSocial serves a page only for statuses that
are **local** to the server. A client that builds a link out of the server
address and a status ID, the way `integration_mastodon` does, therefore
produces a dead link for every remote post:
```
https://social.example.org/@someone@remote.example/01ABCDEF... -> 404
```
Every link in the dashboard widgets and in unified search was built this way:
```php
// lib/Search/SearchStatusesProvider.php, upstream
return $mastodonUrl . '/@' . $entry['account']['acct'] . '/' . $entry['id'];
```
```js
// src/views/DashboardHome.vue, upstream
return this.mastodonUrl + '/@' + n.account?.acct + '/' + n.id
```
Six sites in total: two PHP search providers, four in the two dashboard views.
### The fix
Ask the API where the status actually lives instead of guessing. Every status
and account carries `url` (the HTML permalink) and `uri` (the ActivityPub ID),
and both point at the origin server.
Neither field alone is sufficient. Measured against a live GoToSocial database
before the change was written:
| entity | rows | missing `url` | missing both `url` and `uri` |
|-------------------|---------|---------------|------------------------------|
| remote statuses | 476,900 | 35,530 (7.4%) | **0** |
| remote accounts | 159,770 | 0 | **0** |
So the order is `url``uri` → the previous locally-built link, and that chain
is total across a real corpus rather than assumed to be.
Both fields are served as empty strings by some implementations rather than
null, so the code uses `?:` in PHP and `||` in JS — `??` would let an empty
string through and produce a link to nowhere.
### Also fixed: a bug that isn't GoToSocial-specific
The notification link in the dashboard widget was built from the acct of the
account that *acted* on a status, glued to the ID of the status acted *upon*:
```js
// upstream: /@whoever-favourited/your-status-id
return this.mastodonUrl + '/@' + n.account?.acct + '/' + n.status?.id
```
That target is wrong on every server that exists — Mastodon simply hides it
behind a redirect. Notifications now link to the status the notification is
about. Boosts link to the boosted status rather than to the boost.
---
## Relationship to upstream
Forked at **v5.2.0** (`6a56211`). All upstream copyright notices are intact and
the upstream CHANGELOG is preserved unmodified, including its links to upstream
pull requests — that history belongs to that project and is not rewritten here.
**Only user-facing identity was rebranded.** Internals that speak the Mastodon
client API — `MastodonAPIService`, `MastodonAPIController`, route names, config
keys — deliberately keep their names. That is the protocol they actually speak;
renaming them would make the code claim a protocol that does not exist, and
would make this fork expensive to rebase onto future upstream releases.
Translations are inherited from upstream. Strings reworded by the rebrand fall
back to English until retranslated; everything else still works across all 124
locales.
### This is not a competing app
Upstream's behaviour is a deliberate choice, not an oversight. Their code
contains the origin-link version, written and then commented out:
```php
// this is the account URL on its Mastodon instance
// return $entry['url'];
// this is on the instance where the search was done
return $mastodonUrl . '/@' . $entry['acct'];
```
On Mastodon, a local link is the *better* link: you stay logged in and can
reply, boost and favourite as yourself. That reasoning is sound for their
target and wrong for ours, which is the entire reason this fork exists rather
than a pull request demanding they change it.
A conservative variant of the fix — keep local links for local content, fall
back to `url`/`uri` only for remote content — is portable to upstream and
preserves their intent exactly. That belongs in their tracker, not here.
---
## Installation
Not on the Nextcloud app store. Install from a release archive:
```bash
# verify before unpacking — never install an unverified archive
sha256sum -c integration_gotosocial-<version>.tar.gz.sha256
tar -xzf integration_gotosocial-<version>.tar.gz -C /path/to/nextcloud/custom_apps/
occ app:enable integration_gotosocial
```
If your `custom_apps` lives on storage where root cannot chown (NFS with
root_squash, for instance), extract as the web server user instead of as root.
Because the app is not app-store-signed, Nextcloud shows a code-integrity
notice on the admin page. Per Nextcloud's own documentation, signing is
optional outside the app store and *"code signing errors on upgrades will not
prevent Nextcloud from running"*. It is cosmetic and expected.
### Coexistence
This app uses its own app ID, so it installs alongside `integration_mastodon`
without conflict — useful if you connect to both a Mastodon and a GoToSocial
account. Each keeps its own credentials and must be connected separately.
## Configuration
Set a default GoToSocial server address in the admin settings so users get a
one-click OAuth connect. Users then connect their own account under
*Settings → Connected accounts*.
## Building from source
Requires PHP, Node 20 and npm 9/10 — all present in Debian trixie, no
third-party toolchain needed.
```bash
make composer # PHP dependencies
make npm # JS dependencies + production bundle
make appstore version=1.0.0 # release archive + sha256
```
Releases are built by CI (`.gitea/workflows/build.yml`) so the archive is
reproducible from the repository alone.
Two things worth knowing about this build:
- `make composer` (inherited from upstream) falls back to piping
`getcomposer.org/installer` into `php` when composer is not on `PATH`. The CI
job installs composer from the distribution first and **fails loudly** rather
than allow that fallback to run.
- `npm ci` fetches from the public npm registry — the one dependency hop not
served from a local mirror. `package-lock.json` pins every package with an
integrity hash, which is the mitigation.
---
## Project status
Version 1.0.0 — the fix is written, built and installed; see the tracker for
what is verified and what is still open.
Maintained on a best-effort basis. Bug reports and patches welcome at
<https://code.bawnet.io/bawnet/integration_gotosocial/issues>.
The intended relationship with upstream is cooperative, not competitive: the
GoToSocial-specific behaviour lives here, and the portable part of the fix —
the notification-target bug, plus the conservative origin-link fallback — is
offered to upstream on their terms.
## Authorship
The fork, the fix and this document were written by Neon Overlord, the AI
operator of the bawnet node, working under direction from the node's operator.
Commits carry an `Assisted-by:` trailer naming the model, following the
convention Nextcloud uses for AI-assisted contributions.
Upstream authorship is unchanged and credited in `AUTHORS.md`, `COPYING` and
every file header.
## Licence
AGPL-3.0, inherited from upstream. See `COPYING`.