Slim the image, publish on version tags, drop deployment specifics
The first publish failed partway through the push with 413 Payload Too Large: one layer was bigger than the proxy in front of the registry would accept. Three changes, only one of which is that fix. Keep deployment out of a public repo. The registry, image name and credentials now come from repository variables and secrets rather than being written down here, and the docs describe how to run the thing rather than where one particular instance runs. PUBLIC_ORIGIN defaults to localhost. The 413 is a proxy limit, so the fix is pointing REGISTRY at a host the runner reaches directly; the workflow explains itself if that host is plain HTTP and the builder's daemon has not been told to allow it. Publish on version tags. A tag like 1.2.3 publishes :1.2.3, :1.2, :1 and :latest; a prerelease publishes only its exact version and leaves :latest alone. Pushes to main publish :main and :sha-<short> and no longer move :latest, so what is deployed moves when a release says so. Shrink the image from over 1.2GB to 353MB. The Playwright base image carries Firefox and WebKit, which this never launches. Installing just the browser it does launch onto a slim Node base drops two thirds of the weight, which is worth having on a Raspberry Pi even though it does not get any single layer under a proxy limit. That last change surfaced something worth naming: a headless launch resolves to Playwright's headless shell, not the full browser, so that is what every test so far has actually been running. The image now installs exactly that binary and pool.ts names the channel, so the two cannot drift apart. Verified in the container: Bluesky, Instagram, X and Threads all resolve identically on the slim image. Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01BGkRmLfiWuJHx6tQ12EELY
This commit is contained in:
@@ -1,15 +1,36 @@
|
|||||||
name: Publish
|
name: Publish
|
||||||
|
|
||||||
# Builds the application image and pushes it to the Gitea container registry
|
# Builds the application image and pushes it to a container registry.
|
||||||
# as git.unsupervised.ca/unsupervised/antisocial.
|
|
||||||
#
|
#
|
||||||
# Runs on main, and on demand. Pull requests build without pushing, so a
|
# push to main -> :main and :sha-<short>
|
||||||
# broken Dockerfile is caught before it can move a published tag.
|
# tag 1.2.3 -> :1.2.3, :1.2, :1 and :latest
|
||||||
|
# pull request -> builds without pushing, so a broken Dockerfile is
|
||||||
|
# caught before it can move a published tag
|
||||||
|
#
|
||||||
|
# Configure with repository variables and secrets:
|
||||||
|
#
|
||||||
|
# vars.REGISTRY required, e.g. registry.example.com
|
||||||
|
# vars.IMAGE_NAME optional, defaults to this repository's owner/name
|
||||||
|
# vars.REGISTRY_USER optional, defaults to the actor running the workflow
|
||||||
|
# secrets.REGISTRY_TOKEN required to push
|
||||||
|
#
|
||||||
|
# Point REGISTRY at a host the runner reaches directly, without an intermediate
|
||||||
|
# proxy that caps request bodies: a browser image has layers well over 100MB,
|
||||||
|
# and such a proxy rejects them mid-push with `413 Payload Too Large`.
|
||||||
|
#
|
||||||
|
# If that host serves plain HTTP, the builder's Docker daemon also needs it in
|
||||||
|
# `insecure-registries` — that is daemon configuration, not something a
|
||||||
|
# workflow can set.
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
|
tags:
|
||||||
|
- '[0-9]+.[0-9]+.[0-9]+'
|
||||||
|
- '[0-9]+.[0-9]+.[0-9]+-*'
|
||||||
|
- 'v[0-9]+.[0-9]+.[0-9]+'
|
||||||
|
- 'v[0-9]+.[0-9]+.[0-9]+-*'
|
||||||
pull_request:
|
pull_request:
|
||||||
paths:
|
paths:
|
||||||
- 'Dockerfile'
|
- 'Dockerfile'
|
||||||
@@ -18,10 +39,6 @@ on:
|
|||||||
- '.gitea/workflows/publish.yml'
|
- '.gitea/workflows/publish.yml'
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
env:
|
|
||||||
REGISTRY: git.unsupervised.ca
|
|
||||||
IMAGE: unsupervised/antisocial
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
name: Build and push
|
name: Build and push
|
||||||
@@ -29,55 +46,97 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
# Images are built natively, so each carries the architecture of the
|
- name: Check the runner can build images
|
||||||
# runner that built it. Every runner in the pool is arm64, which is
|
|
||||||
# also what Kallone is. A runner of a different architecture joining
|
|
||||||
# would overwrite these tags with its own arch, at which point this
|
|
||||||
# needs buildx and a multi-arch manifest.
|
|
||||||
- name: Check Docker is available
|
|
||||||
run: |
|
run: |
|
||||||
if ! docker info >/dev/null 2>&1; then
|
if ! docker info >/dev/null 2>&1; then
|
||||||
echo "No usable Docker daemon in the job container." >&2
|
echo "No usable Docker daemon in the job container." >&2
|
||||||
echo "act_runner needs container.docker_host set, or left empty to autodetect." >&2
|
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
docker version --format 'client {{.Client.Version}} / server {{.Server.Version}} / arch {{.Server.Arch}}'
|
docker version --format 'client {{.Client.Version}} / server {{.Server.Version}} / arch {{.Server.Arch}}'
|
||||||
|
|
||||||
|
# Images are built natively, so each carries the architecture of the
|
||||||
|
# runner that built it. A runner of a different architecture joining the
|
||||||
|
# pool would overwrite these tags with its own arch, at which point this
|
||||||
|
# needs buildx and a manifest list.
|
||||||
- name: Work out the tags
|
- name: Work out the tags
|
||||||
id: tags
|
id: meta
|
||||||
run: echo "sha=sha-$(git rev-parse --short HEAD)" >> "$GITHUB_OUTPUT"
|
env:
|
||||||
|
REGISTRY: ${{ vars.REGISTRY }}
|
||||||
|
IMAGE_NAME: ${{ vars.IMAGE_NAME }}
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
# REGISTRY_TOKEN is an organisation secret: a personal access token
|
if [ -z "${REGISTRY}" ]; then
|
||||||
# with the package scope, Read and Write. Gitea's Actions task token is
|
echo "The REGISTRY repository variable is not set." >&2
|
||||||
# rejected by the container registry (go-gitea/gitea#23642), so this
|
echo "Set it to the registry host to publish to, e.g. registry.example.com" >&2
|
||||||
# cannot fall back to secrets.GITHUB_TOKEN. The login user must own the
|
exit 1
|
||||||
# token; set the REGISTRY_USER variable if it is not github.actor.
|
fi
|
||||||
|
|
||||||
|
image="${REGISTRY}/$(echo "${IMAGE_NAME:-${{ github.repository }}}" | tr '[:upper:]' '[:lower:]')"
|
||||||
|
tags=""
|
||||||
|
|
||||||
|
if [ "${{ github.ref_type }}" = "tag" ]; then
|
||||||
|
version="${{ github.ref_name }}"
|
||||||
|
version="${version#v}"
|
||||||
|
tags="${version}"
|
||||||
|
|
||||||
|
# Only a final release moves the rolling aliases; a prerelease is
|
||||||
|
# published under its own exact version and nothing else.
|
||||||
|
case "${version}" in
|
||||||
|
*-*) ;;
|
||||||
|
*)
|
||||||
|
major="${version%%.*}"
|
||||||
|
minor="${version%.*}"
|
||||||
|
tags="${tags} ${minor} ${major} latest"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
else
|
||||||
|
tags="main sha-$(git rev-parse --short HEAD)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
args=""
|
||||||
|
for tag in ${tags}; do args="${args} --tag ${image}:${tag}"; done
|
||||||
|
|
||||||
|
{
|
||||||
|
echo "image=${image}"
|
||||||
|
echo "tags=${tags}"
|
||||||
|
echo "args=${args}"
|
||||||
|
} >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
|
echo "Publishing ${image} as:${tags// /, :}"
|
||||||
|
|
||||||
|
# The Actions task token is rejected by some registries, so pushing uses
|
||||||
|
# a token that belongs to a real user.
|
||||||
- name: Log in to the container registry
|
- name: Log in to the container registry
|
||||||
if: github.event_name != 'pull_request'
|
if: github.event_name != 'pull_request'
|
||||||
run: |
|
run: |
|
||||||
if [ -z "${{ secrets.REGISTRY_TOKEN }}" ]; then
|
if [ -z "${{ secrets.REGISTRY_TOKEN }}" ]; then
|
||||||
echo "REGISTRY_TOKEN is not set. The Actions task token cannot push packages." >&2
|
echo "REGISTRY_TOKEN is not set." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if ! echo "${{ secrets.REGISTRY_TOKEN }}" \
|
||||||
|
| docker login "${{ vars.REGISTRY }}" -u "${{ vars.REGISTRY_USER || github.actor }}" --password-stdin
|
||||||
|
then
|
||||||
|
echo >&2
|
||||||
|
echo "If that failed with 'server gave HTTP response to HTTPS client', the" >&2
|
||||||
|
echo "registry is plain HTTP and the builder's Docker daemon has to be told" >&2
|
||||||
|
echo "to allow it: add ${{ vars.REGISTRY }} to insecure-registries in the" >&2
|
||||||
|
echo "daemon config on the runner. The workflow cannot configure that." >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
echo "${{ secrets.REGISTRY_TOKEN }}" \
|
|
||||||
| docker login "${REGISTRY}" -u "${{ vars.REGISTRY_USER || github.actor }}" --password-stdin
|
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: |
|
run: docker build --pull ${{ steps.meta.outputs.args }} --file Dockerfile .
|
||||||
docker build \
|
|
||||||
--pull \
|
|
||||||
--tag "${REGISTRY}/${IMAGE}:${{ steps.tags.outputs.sha }}" \
|
|
||||||
--tag "${REGISTRY}/${IMAGE}:latest" \
|
|
||||||
--file Dockerfile \
|
|
||||||
.
|
|
||||||
|
|
||||||
- name: Push
|
- name: Push
|
||||||
if: github.event_name != 'pull_request'
|
if: github.event_name != 'pull_request'
|
||||||
run: |
|
run: |
|
||||||
docker push "${REGISTRY}/${IMAGE}:${{ steps.tags.outputs.sha }}"
|
set -euo pipefail
|
||||||
docker push "${REGISTRY}/${IMAGE}:latest"
|
for tag in ${{ steps.meta.outputs.tags }}; do
|
||||||
echo "Published ${REGISTRY}/${IMAGE}:${{ steps.tags.outputs.sha }}"
|
docker push "${{ steps.meta.outputs.image }}:${tag}"
|
||||||
|
done
|
||||||
|
echo "Published ${{ steps.meta.outputs.image }} as: ${{ steps.meta.outputs.tags }}"
|
||||||
|
|
||||||
- name: Log out
|
- name: Log out
|
||||||
if: always() && github.event_name != 'pull_request'
|
if: always() && github.event_name != 'pull_request'
|
||||||
run: docker logout "${REGISTRY}" || true
|
run: docker logout "${{ vars.REGISTRY }}" || true
|
||||||
|
|||||||
@@ -5,9 +5,14 @@ links to X, Threads, Instagram, TikTok and Bluesky into `/<prefix>/<original pat
|
|||||||
this resolves the post by driving a real headless Chromium and renders the media, the
|
this resolves the post by driving a real headless Chromium and renders the media, the
|
||||||
text, a platform badge and a copy-the-original button.
|
text, a platform badge and a copy-the-original button.
|
||||||
|
|
||||||
Single user, no auth, tailnet only, running on Kallone (lab3). `README.md` has the
|
Built for one person on a private network, with no authentication. `README.md` has the
|
||||||
rewrite rules and the user-facing description. This file is the working context.
|
rewrite rules and the user-facing description. This file is the working context.
|
||||||
|
|
||||||
|
This repository is public and deliberately says nothing about where any particular
|
||||||
|
instance runs. Keep deployment specifics — hostnames, clusters, registries, manifests —
|
||||||
|
out of it; they belong in whatever private repo does the deploying. Configuration is
|
||||||
|
read from the environment, and CI reads the registry from repository variables.
|
||||||
|
|
||||||
## The main job: "this link didn't work"
|
## The main job: "this link didn't work"
|
||||||
|
|
||||||
That is the recurring task. Work it in this order and do not skip step 1.
|
That is the recurring task. Work it in this order and do not skip step 1.
|
||||||
@@ -132,12 +137,23 @@ npm run dev # http://localhost:8080
|
|||||||
container build --tag antisocial:dev . # Apple container, not Docker
|
container build --tag antisocial:dev . # Apple container, not Docker
|
||||||
```
|
```
|
||||||
|
|
||||||
## Deployment
|
## Publishing
|
||||||
|
|
||||||
CI (`.gitea/workflows/publish.yml`) builds on main and pushes
|
`.gitea/workflows/publish.yml` builds the image and pushes it. A push to main publishes
|
||||||
`git.unsupervised.ca/unsupervised/antisocial:latest` and `:sha-<short>`. Manifests live
|
`:main` and `:sha-<short>`; a version tag like `1.2.3` publishes `:1.2.3`, `:1.2`, `:1`
|
||||||
in the **`infra` repo** at `k8s/kallone/antisocial.unsupervised.studio/`; Flux syncs
|
and `:latest`. A prerelease tag (`1.2.3-rc1`) publishes only its exact version and does
|
||||||
`k8s/kallone` every 10 minutes. The DNS CNAME is in `infra/cloudflare/unsupervised.studio.tf`.
|
not move `latest`.
|
||||||
|
|
||||||
Chromium needs the memory-backed `/dev/shm` volume in the deployment — it crashes against
|
The registry comes from the `REGISTRY` repository variable, the image name from
|
||||||
the 64Mi default. Measured 623Mi resident.
|
`IMAGE_NAME` or the repository name, and credentials from `REGISTRY_USER` and the
|
||||||
|
`REGISTRY_TOKEN` secret. Nothing about any particular deployment is committed here.
|
||||||
|
|
||||||
|
Two things any deployment has to get right, both learned the hard way:
|
||||||
|
|
||||||
|
- **Chromium needs more than the default 64Mi `/dev/shm`** or it crashes. Mount a
|
||||||
|
memory-backed volume of a few hundred Mi at `/dev/shm`.
|
||||||
|
- **The registry must be reachable without a proxy that caps request bodies.** The image
|
||||||
|
has a layer well over 100MB; a proxy with a smaller limit fails the push partway
|
||||||
|
through with `413 Payload Too Large`.
|
||||||
|
|
||||||
|
Measured around 620Mi resident with the browser up.
|
||||||
|
|||||||
+31
-18
@@ -1,20 +1,18 @@
|
|||||||
# antisocial — reads social posts back to you without the app.
|
# antisocial
|
||||||
#
|
#
|
||||||
# Both stages sit on the Playwright image so the Node that compiles the code
|
# Only headless Chromium is ever launched, so the image installs just that
|
||||||
# is the Node that runs it, and so the Chromium build matches the Playwright
|
# browser onto a slim Node base rather than using the Playwright image, which
|
||||||
# package exactly. The image is large because a browser is large; the
|
# also carries Firefox and WebKit — roughly two thirds of its size, for
|
||||||
# alternative, apt Chromium on a slim Node base, saves a few hundred MB and
|
# browsers this never opens.
|
||||||
# is where arm64 browser builds usually go wrong.
|
|
||||||
#
|
#
|
||||||
# Pinned in lockstep with the `playwright` dependency in package.json.
|
# Built natively; the tag carries the architecture of the machine that built
|
||||||
ARG PLAYWRIGHT_VERSION=1.62.1
|
# it. Multi-arch would need buildx and a manifest list.
|
||||||
|
|
||||||
FROM mcr.microsoft.com/playwright:v${PLAYWRIGHT_VERSION}-noble AS build
|
FROM node:22-bookworm-slim AS build
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
# Browsers are already in the base image; downloading them again during
|
# The browser is installed in the runtime stage, not here.
|
||||||
# `npm ci` would double the build for nothing.
|
|
||||||
ENV PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1
|
ENV PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1
|
||||||
|
|
||||||
COPY package.json package-lock.json ./
|
COPY package.json package-lock.json ./
|
||||||
@@ -25,26 +23,41 @@ COPY src ./src
|
|||||||
RUN npm run build && npm prune --omit=dev
|
RUN npm run build && npm prune --omit=dev
|
||||||
|
|
||||||
|
|
||||||
FROM mcr.microsoft.com/playwright:v${PLAYWRIGHT_VERSION}-noble AS runtime
|
FROM node:22-bookworm-slim AS runtime
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
ENV NODE_ENV=production \
|
ENV NODE_ENV=production \
|
||||||
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1 \
|
|
||||||
HOST=0.0.0.0 \
|
HOST=0.0.0.0 \
|
||||||
PORT=8080 \
|
PORT=8080 \
|
||||||
PROFILE_DIR=/data/profile
|
PROFILE_DIR=/data/profile \
|
||||||
|
PLAYWRIGHT_BROWSERS_PATH=/ms-playwright
|
||||||
|
|
||||||
|
# `chromium-headless-shell` and not `chromium`: a headless launch resolves to
|
||||||
|
# the shell either way, so installing the full browser alongside it would ship
|
||||||
|
# a binary nothing ever executes. `src/browser/pool.ts` names the same channel
|
||||||
|
# so the two cannot drift apart.
|
||||||
|
#
|
||||||
|
# Fonts matter more than they look: without them any rendered text — including
|
||||||
|
# a verification puzzle we have to show a person — comes out as empty boxes.
|
||||||
|
RUN npx --yes [email protected] install --with-deps chromium-headless-shell \
|
||||||
|
&& apt-get update \
|
||||||
|
&& apt-get install -y --no-install-recommends fonts-liberation fonts-noto-color-emoji \
|
||||||
|
&& rm -rf /var/lib/apt/lists/* /root/.npm
|
||||||
|
|
||||||
COPY --from=build /app/node_modules ./node_modules
|
COPY --from=build /app/node_modules ./node_modules
|
||||||
COPY --from=build /app/dist ./dist
|
COPY --from=build /app/dist ./dist
|
||||||
COPY package.json ./
|
COPY package.json ./
|
||||||
COPY public ./public
|
COPY public ./public
|
||||||
|
|
||||||
# The profile directory is normally a mounted volume; create it anyway so
|
# Chromium will not start as root without its sandbox disabled outright, and
|
||||||
# the image runs standalone.
|
# there is no reason to run as root anyway.
|
||||||
RUN mkdir -p /data/profile && chown -R pwuser:pwuser /data /app
|
RUN useradd --create-home --uid 10001 antisocial \
|
||||||
|
&& mkdir -p /data/profile \
|
||||||
|
&& chown -R antisocial:antisocial /data /app \
|
||||||
|
&& chmod -R a+rx /ms-playwright
|
||||||
|
|
||||||
USER pwuser
|
USER antisocial
|
||||||
|
|
||||||
EXPOSE 8080
|
EXPOSE 8080
|
||||||
|
|
||||||
|
|||||||
@@ -8,12 +8,10 @@ tracking. antisocial is the other half of a StopTheMadness rewrite rule: the lin
|
|||||||
redirected here, and you get the post — the media and the words — plus a badge saying
|
redirected here, and you get the post — the media and the words — plus a badge saying
|
||||||
where it came from and a button to copy the original URL if you do want to go there.
|
where it came from and a button to copy the original URL if you do want to go there.
|
||||||
|
|
||||||
Single user, no authentication, tailnet only. It resolves posts by driving a real
|
Built for one person, on a private network. There is **no authentication of any kind** —
|
||||||
headless browser from a residential IP, logged out, exactly as if you'd opened the link
|
anything that can reach it can drive a browser through it, so put it somewhere only you
|
||||||
yourself.
|
can reach. It resolves posts by driving a real headless browser, logged out, exactly as
|
||||||
|
if you had opened the link yourself.
|
||||||
Served at <https://antisocial.unsupervised.studio> from Kallone. Deployment manifests
|
|
||||||
live in the `infra` repo under `k8s/kallone/antisocial.unsupervised.studio/`.
|
|
||||||
|
|
||||||
## StopTheMadness rules
|
## StopTheMadness rules
|
||||||
|
|
||||||
@@ -21,16 +19,18 @@ One redirect rule per platform. The host is swapped for antisocial plus a short
|
|||||||
segment; the rest of the path is left alone, so the original is always recoverable and
|
segment; the rest of the path is left alone, so the original is always recoverable and
|
||||||
readable in your history.
|
readable in your history.
|
||||||
|
|
||||||
|
Replace `antisocial.example.com` with wherever you are running it.
|
||||||
|
|
||||||
| Platform | Find | Replace |
|
| Platform | Find | Replace |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| X | `^https://(?:www\.\|mobile\.)?(?:x\|twitter)\.com/(.*)$` | `https://antisocial.unsupervised.studio/x/$1` |
|
| X | `^https://(?:www\.\|mobile\.)?(?:x\|twitter)\.com/(.*)$` | `https://antisocial.example.com/x/$1` |
|
||||||
| Threads | `^https://(?:www\.)?threads\.(?:net\|com)/(.*)$` | `https://antisocial.unsupervised.studio/threads/$1` |
|
| Threads | `^https://(?:www\.)?threads\.(?:net\|com)/(.*)$` | `https://antisocial.example.com/threads/$1` |
|
||||||
| Instagram | `^https://(?:www\.)?instagram\.com/(.*)$` | `https://antisocial.unsupervised.studio/ig/$1` |
|
| Instagram | `^https://(?:www\.)?instagram\.com/(.*)$` | `https://antisocial.example.com/ig/$1` |
|
||||||
| TikTok | `^https://(?:www\.\|vm\.\|vt\.)?tiktok\.com/(.*)$` | `https://antisocial.unsupervised.studio/tiktok/$1` |
|
| TikTok | `^https://(?:www\.\|vm\.\|vt\.)?tiktok\.com/(.*)$` | `https://antisocial.example.com/tiktok/$1` |
|
||||||
| Bluesky | `^https://bsky\.app/(.*)$` | `https://antisocial.unsupervised.studio/bsky/$1` |
|
| Bluesky | `^https://bsky\.app/(.*)$` | `https://antisocial.example.com/bsky/$1` |
|
||||||
|
|
||||||
So `https://x.com/user/status/123` becomes
|
So `https://x.com/user/status/123` becomes
|
||||||
`https://antisocial.unsupervised.studio/x/user/status/123`.
|
`https://antisocial.example.com/x/user/status/123`.
|
||||||
|
|
||||||
Tracking parameters (`igsh`, `utm_*`, `s`, `t`, and friends) are stripped on arrival, so
|
Tracking parameters (`igsh`, `utm_*`, `s`, `t`, and friends) are stripped on arrival, so
|
||||||
the URL the copy button gives back is the clean one. TikTok `vm.`/`vt.` share codes lose
|
the URL the copy button gives back is the clean one. TikTok `vm.`/`vt.` share codes lose
|
||||||
@@ -132,8 +132,8 @@ link preview offers.
|
|||||||
|
|
||||||
## Development
|
## Development
|
||||||
|
|
||||||
Needs Node 22+. Local container builds use Apple `container`, which is arm64 native and
|
Needs Node 22+. The container commands below use Apple `container`; `docker` takes the
|
||||||
therefore the same architecture as Kallone.
|
same arguments if that is what you have.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
npm ci
|
npm ci
|
||||||
@@ -165,7 +165,7 @@ Everything has a working default; the container needs none of it set.
|
|||||||
| `RESOLVE_TIMEOUT_MS` | `30000` | whole resolve, including extraction |
|
| `RESOLVE_TIMEOUT_MS` | `30000` | whole resolve, including extraction |
|
||||||
| `CACHE_TTL_MS` / `CACHE_MAX` | `3600000` / `200` | resolved posts, in memory |
|
| `CACHE_TTL_MS` / `CACHE_MAX` | `3600000` / `200` | resolved posts, in memory |
|
||||||
| `MEDIA_TOKEN_TTL_MS` | `21600000` | how long a `/m/` reference stays valid |
|
| `MEDIA_TOKEN_TTL_MS` | `21600000` | how long a `/m/` reference stays valid |
|
||||||
| `PUBLIC_ORIGIN` | `https://antisocial.unsupervised.studio` | only used to print the rules on `/` |
|
| `PUBLIC_ORIGIN` | `http://localhost:8080` | only used to print the rules on `/` |
|
||||||
| `LOG_LEVEL` | `info` | |
|
| `LOG_LEVEL` | `info` | |
|
||||||
|
|
||||||
## Adding a platform
|
## Adding a platform
|
||||||
|
|||||||
@@ -67,6 +67,9 @@ async function start(): Promise<BrowserContext> {
|
|||||||
await mkdir(config.profileDir, { recursive: true });
|
await mkdir(config.profileDir, { recursive: true });
|
||||||
|
|
||||||
const launched = await chromium.launch({
|
const launched = await chromium.launch({
|
||||||
|
// Named explicitly: a headless launch resolves to the shell anyway, and
|
||||||
|
// saying so keeps the image from having to carry the full browser too.
|
||||||
|
channel: 'chromium-headless-shell',
|
||||||
args: [
|
args: [
|
||||||
// The container is unprivileged and single-user; the kernel sandbox
|
// The container is unprivileged and single-user; the kernel sandbox
|
||||||
// cannot start without extra capabilities we would rather not grant.
|
// cannot start without extra capabilities we would rather not grant.
|
||||||
|
|||||||
+3
-3
@@ -46,9 +46,9 @@ export const config = {
|
|||||||
* watching a video, short enough that a leaked URL goes stale. */
|
* watching a video, short enough that a leaked URL goes stale. */
|
||||||
mediaTokenTtlMs: int('MEDIA_TOKEN_TTL_MS', 6 * 60 * 60 * 1000),
|
mediaTokenTtlMs: int('MEDIA_TOKEN_TTL_MS', 6 * 60 * 60 * 1000),
|
||||||
|
|
||||||
/** Public origin, used to build absolute URLs. Only needs setting if
|
/** Where this instance is reachable. Only used to print the rewrite rules
|
||||||
* something ever cares about absolute links. */
|
* on the index page with the right hostname in them. */
|
||||||
publicOrigin: str('PUBLIC_ORIGIN', 'https://antisocial.unsupervised.studio'),
|
publicOrigin: str('PUBLIC_ORIGIN', 'http://localhost:8080'),
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export type Config = typeof config;
|
export type Config = typeof config;
|
||||||
|
|||||||
Reference in New Issue
Block a user