Skip to content

feat: add LinkedGrow satellite - #259

Open
DigiHold wants to merge 1 commit into
av:mainfrom
DigiHold:feat/linkedgrow
Open

feat: add LinkedGrow satellite#259
DigiHold wants to merge 1 commit into
av:mainfrom
DigiHold:feat/linkedgrow

Conversation

@DigiHold

@DigiHold DigiHold commented Sep 5, 2026

Copy link
Copy Markdown

Adds LinkedGrow as a satellite, following .agents/skills/new-service/SKILL.md. Handle linkedgrow, port 35080, docs at 2.3.98.

What it is

AI agents that find leads and clients on LinkedIn. You describe who you sell to, an agent scores each profile it finds, sends the invitation and runs the follow up conversation, then hands over the people who reply. The posting side comes with it. Source at https://github.com/DigiHold/LinkedGrow under AGPL-3.0. I maintain it, so say so if you would rather not carry a service submitted by its own author.

Every action happens in a real Chrome under Xvfb, because LinkedIn has no API for reading a feed or sending a message. That is what the resource notes in the doc are about.

Three decisions that depart from the skill, each with a reason

  • Named volumes instead of a HARBOR_LINKEDGROW_WORKSPACE bind mount. I wrote the workspace version first, with a linkedgrow-init sidecar chowning the directories the way linkwarden-init does. It does not work here: linkwarden runs the app as ${HARBOR_USER_ID}, so the chown target matches, while both LinkedGrow images run as uid 10001 and the app exits with /data/config is not writable by uid 10001 in a restart loop. I only found that by booting it. Named volumes inherit the image's ownership on creation, so the whole failure mode disappears and one container goes with it. HARBOR_LINKEDGROW_WORKSPACE is therefore not defined at all rather than shipped unused. Postiz's elasticsearch volume is the existing precedent for a named volume in services/.
  • restart: unless-stopped. The app applies migrations and writes the secrets the worker reads, and the worker blocks on its health check, so an app that stays down strands the queue. Same reasoning the linkwarden and windmill files already state in a comment.
  • No compose.x.linkedgrow.ollama.yml. The AI client calls Anthropic, OpenAI, Google, xAI and Moonshot at hardcoded addresses and has no configurable base URL, so a Harbor backend cannot be pointed at it today. Rather than ship a cross-file that silently does nothing, the doc says plainly that this is the one integration a Harbor user expects and does not get.

Checks run

  • docker compose --env-file .env -f compose.yml -f services/compose.linkedgrow.yml config exits 0, no unset variable warnings
  • Booted it: linkedgrow reaches healthy, /api/health answers {"ok":true,"edition":"self-hosted","setupCompleted":false}, and the container runs as uid 10001 with secrets.env written -rw------- linkedgrow linkedgrow into the config volume
  • End user workflow, with Playwright against the running service: /sign-up creates the owner account, which lands on the setup wizard. The screenshot in the doc is that screen, 1280x800.
  • tsc --noEmit clean on app/src/serviceMetadata.ts

Two things I did not do, stated rather than glossed. I did not run harbor config update, because I edited profiles/default.env from a checkout rather than an installed Harbor; the rendered .env I validated against was a copy of default.env. And I did not exercise the actual LinkedIn workflow end to end, because that needs credentials for a real account plus an address reserved for it, which is not something to do from a throwaway container. The wizard also refuses to complete without an AI key, so the screenshot stops at the first screen rather than a populated dashboard.

Logo is the favicon URL in the metadata entry; run harbor dev add-logos over it if you want it resolved your way.

AI agents that find leads and clients on LinkedIn, driving a real Chrome
per connected account under Xvfb because LinkedIn has no API for reading
a feed or sending a message. Handle linkedgrow, port 35080, docs at
2.3.98.

Three containers: the app, a libSQL server, and the worker that owns the
browsers. Named volumes rather than a workspace bind mount, because both
images run as uid 10001 and refuse to start on a directory they cannot
write; a fresh named volume inherits the image's ownership, while a bind
mount target is created root owned and a chown sidecar does not fix it
portably (tested, it does not survive Docker Desktop's mount layer).

No ollama cross-file: the AI client calls its five providers at their own
addresses and has no configurable base URL, so a Harbor backend cannot be
pointed at. Documented rather than left for the user to discover.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant