Just your friends’ posts — chronological, photos included. No ads, no suggested posts, no algorithm.
Free — no charge, no trial, no catch. Your data never touches my server. I can’t see it — by design.
Somewhere along the way, the feed became something else entirely.
Births, moves, milestones — the stuff you’d never want to miss — buried under whatever the ranking model decided would keep you scrolling.
Scroll for a minute and count how many posts are from people you actually know. The feed is inventory now — sold to advertisers, padded with strangers.
What you see is decided by an algorithm optimizing for ad revenue, not for keeping you close to the people you care about.
No installs, no terminals, no servers to babysit. If you have an AI assistant, you’re already equipped. And it’s free — your data lives in your own database on Supabase’s free tier, so there’s nothing to pay for.
Don’t have Muse yet? Join here and use referral code ZG4C9K in Settings within 48 hours — we both get bonus tokens.
Show your Muse this website and tell it to follow the instructions. It reads the recipe below, connects your Facebook, builds your private database, and keeps it fresh every hour.
Your Muse sets up your private feed — just your friends’ posts, kept fresh every hour. You don’t install anything, configure anything, or touch a single setting.
When it’s done, your Muse hands you a personal link. Click it — your feed appears. Bookmark it so you never lose it.
Your posts live in your database. Your key lives in your browser. This page is just static files. I collect absolutely none of your data.
There’s nothing to sign up for. The key is the account — lose it and your Muse just makes you a new one.
No analytics, no cookies phoning home, no pixels. The page loads and then minds its own business.
Tell your Muse “delete my FBFriends backend” and everything is gone. Nothing of yours was ever here to begin with.
If you’re a person, skip this — your Muse has it. The complete technical specification is below: database, Facebook connection, hourly refresh, photos, and the key.
You are setting up FBFriends for the user. This is the complete technical specification. Follow every step in order. When you finish, report back to the user in plain language with no jargon.
=== PART 1: DATABASE ===
1. Create a Supabase project (free tier). Name it FBFriendsDB or similar. Save the project URL and both keys (anon/public key and service_role key). The service_role key is secret — never put it in the browser, in chat, or in any file the user sees.
2. Create a table named `posts` with exactly these columns:
- post_key TEXT PRIMARY KEY (the Facebook post ID)
- author_name TEXT
- author_url TEXT
- text TEXT (the post body)
- created_at TIMESTAMPTZ
- permalink_url TEXT (link to view the post on Facebook)
- source TEXT ('friend' or 'group')
- group_name TEXT (null for friend posts)
- reaction_count INTEGER
- comment_count INTEGER
- media_type TEXT
- media_status TEXT
- photo_urls TEXT[] (array of displayable image URLs, max 2)
- media_ocr TEXT
- ingested_at TIMESTAMPTZ DEFAULT now()
Create a descending index on created_at.
3. Enable Row Level Security on the posts table. Create exactly ONE policy: allow SELECT to the anon role. Do NOT create any write policy for anon. All writes use the service_role key from your backend only.
4. Test: insert one row via the service_role key, read it back via the anon key through the REST API (GET {url}/rest/v1/posts?select=post_key&limit=1 with apikey and Authorization: Bearer headers set to the anon key). Then delete the test row.
=== PART 2: FACEBOOK CONNECTION ===
5. You need read access to the user's Facebook feed. Use whatever method you have: the official Graph API, a CLI tool, browser automation, or a manual export the user provides. You need:
- The user's friends' posts (author name, post text, timestamp, permalink, reaction/comment counts)
6. Pull the most recent 30 days of the user's friends' posts. For each post, capture every field in the schema above. Set source='friend' and group_name=null for all posts.
7. Respect Facebook's rate limits. Space your requests at least 3 seconds apart. If you hit a rate limit error, stop and retry later — do not hammer the API.
=== PART 3: INITIAL LOAD ===
8. Upsert all pulled posts into the posts table via the service_role key. Use post_key as the conflict target (insert new, update existing). Verify the row count matches what you pulled.
=== PART 4: HOURLY REFRESH (the recurring update) ===
9. Set up a scheduled task that runs every hour. Each run must:
a. Pull new/updated posts from Facebook (same method as step 5-6, but only posts newer than the last run — track a watermark timestamp so you don't re-pull everything).
b. Upsert them into the posts table via the service_role key.
c. Delete posts older than 30 days (prune by created_at).
d. Log what it did (posts added, posts pruned, any errors).
e. If Facebook rate-limits you, back off and retry on the next hourly run — never skip the watermark update on a failed run (or you'll re-pull the same posts forever).
10. The watermark: store the timestamp of the most recent successfully ingested post. Each run pulls posts newer than the watermark, then advances the watermark only after a successful upsert. If a run fails, the watermark stays put so the next run retries the same window.
=== PART 5: PHOTOS ===
11. If a post has photos, store up to 2 displayable image URLs per post in photo_urls. Only process posts from the last 7 days.
CRITICAL: Facebook gives you photo PAGE URLs (like https://www.facebook.com/photo.php?fbid=...). These are NOT displayable — a browser cannot render them as images. You MUST resolve each one to a direct image URL:
- Visit the photo page URL in a browser (you must be signed in to Facebook)
- Extract the direct image URL from the og:image meta tag or the main
src
- It will look like https://scontent-*.fbcdn.net/*/*.jpg (a fbcdn.net URL)
- Store the FULL URL including all query parameters — the signed tokens are required
- Do this for the FIRST 2 photos per post only
If you can't resolve a direct image URL, leave photo_urls empty — text always works. Never store a photo.php page URL in photo_urls.
CRITICAL: When updating existing posts, NEVER overwrite a working direct image URL (fbcdn.net) with a photo.php page URL. If the database already has a displayable image URL for a post, keep it.
=== PART 6: THE KEY ===
12. Generate the user's FBFriends key: take the JSON {"u":"","k":""}, encode it as base64url (URL-safe base64, no padding, no line breaks). This is the key string.
CRITICAL: The key MUST be the base64url-encoded JSON object, NOT the raw API key. The JSON has exactly two fields: "u" (the Supabase project URL, e.g. "https://xyz.supabase.co") and "k" (the publishable key starting with "sb_publishable_", or the legacy anon JWT). If you give the user just the raw key string, the app will reject it.
13. Verify the key works: decode it yourself, then make a test REST call to {u}/rest/v1/posts?select=post_key&limit=1 with the anon key in both the apikey header and Authorization: Bearer header. If it returns data, the key is good.
=== PART 7: HANDOFF TO THE USER ===
14. Give the user their PERSONAL LINK (not just the key). Build the URL: https://fbfriends.lvmt.net/app#key=. Give them this full clickable link. Tell them in plain words (no jargon):
"Your feed is ready. Click this link — your feed will load right away. Bookmark it so you never lose it. It works on your phone and computer."
15. Tell them: if they get a new device, they'll paste the key again. If they ever want to cut off access, you can rotate the anon key in Supabase and the old key stops working.
FBFriends shows your friends' posts, pulled from Facebook every hour. A few honest limitations:
FBFriends is a working demo of the AI systems I build — the same craft, running live, on your own data. If an AI can safely and quietly automate and serve your entire Facebook feed, imagine what else it can do.
Lost your personal link? Your Muse can give you the key again — paste it here once and we’ll remember it on this device.
Your key is stored only in this browser (never on our servers). Uncheck to keep it for this session only.