Skip to content

sync(master): align with merged outlaw-dame/memory state#68

Merged
outlaw-dame merged 20 commits into
masterfrom
feature/platform-adaptive-icons
May 8, 2026
Merged

sync(master): align with merged outlaw-dame/memory state#68
outlaw-dame merged 20 commits into
masterfrom
feature/platform-adaptive-icons

Conversation

@outlaw-dame

Copy link
Copy Markdown
Collaborator

Summary

Sync AmoreTechLllc/memory master with the already-merged state from outlaw-dame/memory.

This includes:

  • The Memory API URL hardening work (Safe Browsing + URL guards + secureFetch)
  • Fork-side master commits previously unique to outlaw-dame/memory
  • Conflict reconciliation performed on feature/platform-adaptive-icons

Why

Keeps one clean, easy-to-follow canonical history across your active forks and removes ambiguity about where current work lives.

Validation

outlaw-dame and others added 20 commits March 23, 2026 13:09
Adds server.allowedHosts: true so the app works when accessed via
tunnelled/proxied URLs (Manus sandbox, ngrok, Cloudflare Tunnel, etc.)
docker-compose.local.yml:
  - Add Fuseki (RDF triple store), Redis, and ActivityPods backend services
  - ActivityPods backend runs on host network (port 3000) so it can reach
    the Memory API at localhost:8794 — matches original dev docker-compose
  - PostgreSQL, Memory API, and Vite frontend remain on bridge network
  - Mastopod sidecar uses host network to reach Memory API webhook

api/src/types/enums.ts:
  - Replace fixed t.Enum({ 'http://localhost:3000': ... }) with t.String()
  - Accepts any valid http:// or https:// URL as pod provider endpoint
  - Enables connecting to external providers (activitypods.org, etc.)

frontend/src/components/SignInForm.vue:
  - Show 'Pod Provider URL' field (default: http://localhost:3000)
  - Add async/await + try/catch error handling
  - Show inline error message on failure

frontend/src/components/SignupForm.vue:
  - Show 'Pod Provider URL' field (default: http://localhost:3000)
  - Clear test-only pre-filled values (username/email/password)
  - Add endpoint URL validation
  - Show general error message on failure
…harden stores and views

Replaces boxicons with @lucide/vue for iOS/macOS/web and Material Symbols
for Android, using the Font Loading API to ensure graceful fallback.
Adds AppIcon.vue component, AppIcon.types.ts, and usePlatform.ts composable.

Hardens authStore and postsStore ESLint `no-explicit-any` suppression to
inline disable comments on the correct `}) as any` lines. Fixes v-if+v-for
co-location in NotificationsView, removes unused computed in MessagesView,
and drops unused drizzle-orm import from useFollow.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds elysiaCompat.ts exporting signedIn / signedInGuard so route options
can spread the macro consistently. Renames the setup.ts derive helper
from error() to status() (matches Elysia 1.4's renamed handler context)
and updates all consumers to call status(N, msg). Converts route
detail: 'X' shorthand to detail: { description: 'X' } per Elysia 1.4
schema. No behavior, log message, or response-shape changes.
Adds 0015 migration introducing media_attachments (uuid pk, FK to users
and posts, state/kind enums with check constraints, focus/blurhash/
dimensions/duration metadata, canonical+gateway URL, filebaseCid,
digestMultibase, expiresAt, three indexes) and extends posts with
clientPostKey/clientPostRequestHash plus a partial unique index for
client-side idempotency. Schema-only; consumers added in later commits.
- Adds services/MediaAttachments.ts with create/find/markAttached helpers,
  request hashing, idempotency-key normalization, attachable-id
  resolution, and the MediaAttachmentError sentinel.
- Adds services/MediaAttachments.test.ts covering the helpers above.
- Extends services/ActivityPod with static uploadMedia(user, file, slug)
  posting to <endpoint>/<userName>/data/semapps/file with bearer auth
  and a 5-minute in-process profile cache (invalidated by updateProfile).
- Adds routes/mediaUploads.ts (authenticated upload endpoint) and
  routes/mediaSidecarCallback.ts (sidecar finalize webhook). Plugins
  are not yet registered; wiring lands in the next commit.
postPayload.ts: adds MAX_MEDIA_ATTACHMENTS, MEDIA_EXTENSION_MIME_TYPES,
SUPPORTED_ATTACHMENT_MEDIA_TYPES regex, extractMediaAttachments(),
mergeMediaAttachments(), and an attachments field on
BuildOutboxPostInput.

routes/posts.ts: requires content-or-attachment per request, resolves
attachment IDs into media rows, marks them attached after persistence,
and threads idempotency: hashes the request, looks up an existing
post via clientPostKey, returns the existing post on match and 409
on hash conflict, otherwise inserts a new row.
Registers mediaUploadsPlugin (protected) and mediaSidecarCallbackPlugin
(public) on the Elysia app, exports them from routes/index.ts, adds
media-related translation keys in i18n.ts, and surfaces media DTOs in
types/index.ts.
…ication

Applies the elysiaCompat patterns (signedInGuard, error→status helper,
detail object, ...signedIn route option) across atBridge.ts.

Also clarifies the /subscribe AT-source registration response: Memory
stores AT source metadata for UI visibility only; firehose runtime
ownership lives in fedify-sidecar. Adds a warning field and adjusts
the success message and response schema accordingly.
Aligns ORM/CLI versions with the schema features used in 0015 (uuid pk
+ check constraints + partial unique index syntax).
…le install

- docker-compose.local.yml: move the bundled fedify-sidecar service
  behind a 'legacy-at-harness' compose profile so the default
  Memory dev stack no longer spins it up.
- docker/api.dockerfile: pass --ignore-scripts to bun install to skip
  postinstall scripts during image build for reproducibility.
- Adds frontend/public/manifest.json with name/icons/theme metadata.
- Adds frontend/src/platform/capabilities.ts surfacing runtime feature
  detection used by the composer and media flows.
- Updates frontend/index.html to link the manifest and apple-touch-icon,
  add theme-color and mobile-web-app meta tags, and self-close void
  elements.
- CreatePostForm.vue: media attachment uploader UI, idempotency-key
  generation, and content-or-attachment guard.
- UnifiedFeedItem.vue / ThreadSummary.vue / PostPoll.vue: render media
  attachments (image/gif/video) with focus + alt-text + blurhash hints.
- GifPicker.vue + composables/useKlipy.ts: align with composer media
  pipeline.
- types/api.ts: media DTOs and idempotency fields on post requests.
- i18n/messages.ts + i18n/index.ts: media + idempotency translation
  keys.
…tyles

Refreshes SignInForm.vue, frontend/src/db/localDb.ts, router/index.ts,
embed/pglite workers, and theme styles (style.scss, vusax-overrides.scss,
welcome.scss) so they remain consistent with the media + PWA changes
that landed in earlier commits.
Mirror the ActivityPods backend / Fedify sidecar Safe Browsing v5alpha1
contract on Memory's link-preview surface so the three components share
the same URL hygiene posture.

- New api/src/utils/safeBrowsing.ts with passesSafeBrowsing(url):
  - Reads GOOGLE_SAFE_BROWSING_API_KEY (preferred) or SAFE_BROWSING_API_KEY.
  - Disabled (returns true) when no key configured.
  - 2.5s timeout via AbortSignal.timeout; fail-open by default.
  - SAFE_BROWSING_FAIL_CLOSED=1 inverts behavior on transport / parse errors.
  - Rejects non-http(s) urls outright.

- LinkPreviewService.fetchLinkPreview now:
  - Calls passesSafeBrowsing on the user-supplied URL before issuing the
    fetch, and again on the post-redirect URL when it differs.
  - sanitizeHttpUrl tightened against SSRF:
    * IPv6 unspecified / loopback / link-local (fe80::/10) /
      ULA (fc00::/7) / multicast (ff00::/8) / IPv4-mapped (::ffff:*).
    * IPv4 0.0.0.0/8, CGNAT 100.64/10, doc ranges 192.0.0.0/24,
      192.0.2.0/24, 198.18.0.0/15, 192.88.99.0/24, 203.0.113.0/24,
      and class D/E (224.0.0.0/3).
    * Numeric / hex hostnames (decimal IP encodings such as
      http://2130706433/) are rejected.
  - sanitizeHttpUrl exported for unit testing and reuse.

- Route handler maps the new error categories (numeric host / safe
  browsing / credentials) to HTTP 400.

- Tests: 21 new bun:test cases covering Safe Browsing fail modes,
  env-var precedence, and every SSRF guard branch. Full suite: 153/153.

- .env.example documents GOOGLE_SAFE_BROWSING_API_KEY and
  SAFE_BROWSING_FAIL_CLOSED.

Companion to AP backend commit 3831fb90 (Safe Browsing on opengraph
fetcher) — same env contract, same fail-open default.
Address the residual risks called out alongside the link-preview Safe
Browsing landing:

1. DNS rebinding via redirect: 'follow'.
2. ApRemoteIngestionService.cacheApActorIfStale fetched federation actor
   URIs through raw fetch with no scheme / private-IP / Safe Browsing
   guards.
3. extractMediaUrl stored avatar/banner URLs after only an isHttpsUrl
   check, allowing federated actors to point browsers at private hosts.

Changes
-------
* api/src/utils/urlGuards.ts (new) — single source of truth for URL
  hygiene primitives:
  - sanitizeHttpUrl: rejects non-http(s), userinfo, IPv4 0/8, RFC1918,
    127/8, 169.254/16 (incl. AWS IMDS), CGNAT 100.64/10, doc/benchmark
    ranges (192.0.0/24, 192.0.2/24, 198.18/15, 192.88.99/24, 203.0.113/24),
    multicast/E (224-255/4), IPv6 ::, ::1, fe80::/10, fc00::/7, ff00::/8,
    ::ffff: mapped, and decimal/hex IP-literal hostnames.
  - assertPublicHost: async DNS lookup that validates *every* returned
    A/AAAA record against the same IP blocklist, mitigating the common
    DNS-rebind case where a public hostname resolves to private space.
  - isPublicHttpUrl: synchronous predicate for storage paths that should
    not fetch but still need to refuse private/loopback URLs.
  - UrlGuardError typed code so callers can branch.
* api/src/utils/secureFetch.ts (new) — outbound HTTP wrapper:
  - sanitize -> assertPublicHost (DNS) -> Safe Browsing -> fetch with
    redirect: 'manual'. Each redirect Location is run through the full
    pipeline before the next hop. Caps redirects at 5 by default.
  - Honors caller AbortSignal and a per-call timeoutMs (default 4500ms).
  - Documents the residual rebind window (Bun fetch lacks a connect-
    pinning hook); flagged for follow-up when the runtime moves to Node.
* LinkPreviewService.ts: replaces inline sanitize + manual fetch with
  secureFetch; old sanitizeHttpUrl re-exported as a thin wrapper for
  test compatibility.
* ApRemoteIngestionService.ts: cacheApActorIfStale now uses secureFetch
  (so AP actor fetches go through scheme/IP/SB checks); extractMediaUrl
  validates avatar/banner URLs with isPublicHttpUrl, so a federated
  actor can no longer plant http://10.0.0.1/ in the actor cache.
* routes/linkPreview.ts: maps new error categories (dns/redirect/too many)
  to HTTP 400.

Tests
-----
* api/src/utils/urlGuards.test.ts (new, 5 cases) — predicate coverage.
* api/src/utils/secureFetch.test.ts (new, 11 cases) — pre-fetch guards,
  manual redirect chain, redirect-target re-validation (private IPs +
  non-http(s)), redirect cap, AbortSignal pass-through, Safe Browsing
  per-hop.
* Existing LinkPreviewService.test.ts and safeBrowsing.test.ts still
  pass against the refactored implementation.
* Full bun test: 169/169 pass. tsc --noEmit clean.
@outlaw-dame outlaw-dame merged commit cb8263d into master May 8, 2026
1 check passed

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8af7e10b04

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread docker-compose.local.yml
REDIS_HOST: redis
REDIS_PORT: 6379
# ActivityPods uses host networking so it can reach the app backend at localhost:8794
network_mode: host

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Keep ActivityPods on the Compose network

In docker-compose.local.yml I checked the new activitypods service: it configures SEMAPPS_JENA_URL=http://fuseki:3030 and REDIS_HOST=redis, but network_mode: host takes the container out of the Compose network where those service-name DNS records exist. Docker's Compose reference also notes that port mapping must not be used with network_mode: host, so docker compose -f docker-compose.local.yml up will either reject this service or start it unable to resolve Fuseki/Redis; remove host mode or point these dependencies at host-reachable addresses.

Useful? React with 👍 / 👎.

Comment on lines +98 to +99
if (!endpoint.value.startsWith('http')) {
errorMessages.value.endpoint = 'Must be a valid http:// or https:// URL'

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reject unsupported pod providers before submitting

This validation accepts any http... URL, and the form even suggests https://activitypods.org, but the API schema for signup only accepts viablePodProviders built from localhost plus configured env endpoints (api/src/types/enums.ts). With the default stack, entering the suggested public URL passes client validation and then gets rejected by TypeBox before the route runs; authStore.signup ignores non-500 failures, so the user sees no actionable error. Validate against the same configured provider set or surface the API rejection instead of accepting arbitrary URLs here.

Useful? React with 👍 / 👎.

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