Monorepo for Tangled

docs: add fully offline development guide #7

open opened by nolith.dev targeting master from local-dev

Document the TANGLED_VM_LOCAL_DEV=1 workflow that runs PLC, PDS, and Jetstream inside the dev VM for fully offline development. Covers prerequisites, setup steps, the bootstrap process, and how to log in with the local dev account.

AI-assisted: GitLab Duo Agentic Chat (Claude Opus 4.6) Signed-off-by: Alessio Caiazza code.git@caiazza.info

Labels

None yet.

assignee

None yet.

Participants 1
AT URI
at://did:plc:nzep3slobztdph3kxswzbing/sh.tangled.repo.pull/3mgmn4naui622
+189
Diff #0
+189
docs/DOCS.md
··· 1622 1622 `services.tangled.spindle.enable` (or 1623 1623 `services.tangled.knot.enable`) to `false`. 1624 1624 1625 + ## Fully offline development 1626 + 1627 + By default, the dev VM connects to the public AT Protocol 1628 + infrastructure (plc.directory, Bluesky's Jetstream, etc.), 1629 + which requires internet access and an existing AT Protocol 1630 + account. For fully offline development, you can run the 1631 + entire AT Protocol stack inside the VM. 1632 + 1633 + This mode runs PostgreSQL, a PLC directory server, a PDS 1634 + (Personal Data Server), and Jetstream alongside the knot and 1635 + spindle services. A bootstrap service automatically creates 1636 + a local dev account and default label definitions. 1637 + 1638 + ### Prerequisites 1639 + 1640 + You need Nix installed and a working dev shell (see above). 1641 + macOS users still need a Linux builder for the VM. The first 1642 + build will take a while as it fetches and builds PLC, PDS, 1643 + and Jetstream packages; subsequent runs use the Nix cache. 1644 + 1645 + ### Quick start 1646 + 1647 + The simplest way to start the full offline stack is with the 1648 + tmux orchestrator: 1649 + 1650 + ```bash 1651 + nix develop --impure 1652 + nix run .#local-dev 1653 + ``` 1654 + 1655 + This launches a tmux session with four panes: 1656 + 1657 + - **VM** — boots the NixOS VM with the full AT Protocol stack 1658 + - **Redis** — local redis server for the appview 1659 + - **Appview** — waits for the VM bootstrap, then starts the 1660 + appview watcher with label defaults configured 1661 + - **Tailwind** — CSS watcher 1662 + 1663 + The appview pane automatically waits for the VM to finish 1664 + bootstrapping, reads the owner DID, and configures the 1665 + label defaults before starting. Once ready, open 1666 + http://localhost:3000 and log in with the handle `dev.test` 1667 + and password `password`. 1668 + 1669 + Use `Ctrl-b` + arrow keys to switch between tmux panes. 1670 + `Ctrl-b d` detaches from the session (processes keep 1671 + running). Re-attach with `tmux attach -t tangled-dev`. 1672 + 1673 + ### Manual startup (alternative) 1674 + 1675 + If you prefer separate terminal windows or don't use tmux: 1676 + 1677 + 1. Enter the dev shell with local-dev mode enabled: 1678 + 1679 + ```bash 1680 + TANGLED_VM_LOCAL_DEV=1 nix develop --impure 1681 + ``` 1682 + 1683 + The shell hook will automatically set the appview's 1684 + environment variables to point at the local PLC, PDS, 1685 + and Jetstream: 1686 + 1687 + ``` 1688 + local-dev: appview configured for local AT Protocol stack 1689 + ``` 1690 + 1691 + 2. Start the VM (in the dev shell): 1692 + 1693 + ```bash 1694 + nix run --impure .#vm 1695 + ``` 1696 + 1697 + The VM will boot and you'll see the bootstrap output: 1698 + 1699 + ``` 1700 + ======================================== 1701 + Tangled Local Dev Bootstrap Complete 1702 + Owner DID: did:plc:xxxxxxxxxxxxxxxxxxxxxxxxx 1703 + Handle: dev.test 1704 + Password: password 1705 + PDS: http://localhost:2583 1706 + ======================================== 1707 + ``` 1708 + 1709 + The owner DID is automatically assigned to the knot and 1710 + spindle services. 1711 + 1712 + 3. In a separate terminal (also in the dev shell), start 1713 + the appview: 1714 + 1715 + ```bash 1716 + TANGLED_VM_LOCAL_DEV=1 nix develop --impure 1717 + nix run .#watch-appview 1718 + ``` 1719 + 1720 + Note: you will need to set `TANGLED_LABEL_DEFAULTS` 1721 + manually. After the VM bootstrap completes, read the 1722 + owner DID from `nix/vm-data/atproto/owner-did` and 1723 + construct the label URIs: 1724 + 1725 + ```bash 1726 + did=$(cat nix/vm-data/atproto/owner-did) 1727 + export TANGLED_LABEL_DEFAULTS="at://$did/sh.tangled.label.definition/wontfix,at://$did/sh.tangled.label.definition/good-first-issue,at://$did/sh.tangled.label.definition/duplicate,at://$did/sh.tangled.label.definition/documentation,at://$did/sh.tangled.label.definition/assignee" 1728 + nix run .#watch-appview 1729 + ``` 1730 + 1731 + 4. Open http://localhost:3000 and log in with the handle 1732 + `dev.test` and password `password`. 1733 + 1734 + ### Port mapping 1735 + 1736 + In offline mode, these ports are forwarded from the VM to 1737 + the host: 1738 + 1739 + | Port | Service | 1740 + |------|------------| 1741 + | 2222 | SSH | 1742 + | 2582 | PLC server | 1743 + | 2583 | PDS | 1744 + | 6008 | Jetstream | 1745 + | 6444 | Knot | 1746 + | 6555 | Spindle | 1747 + 1748 + ### How it works 1749 + 1750 + The `TANGLED_VM_LOCAL_DEV=1` flag enables two things: 1751 + 1752 + 1. **Inside the VM**: PostgreSQL, PLC, PDS (patched for 1753 + HTTP), Jetstream, and a bootstrap service are started 1754 + alongside knot and spindle. All services communicate 1755 + over localhost. The bootstrap service creates a 1756 + `dev.test` account on the PDS, creates the default 1757 + label definitions, and provides the DID to knot/spindle 1758 + as their owner. 1759 + 1760 + 2. **On the host**: The dev shell sets environment variables 1761 + so the appview connects to the local PLC 1762 + (`http://localhost:2582`), PDS (`http://localhost:2583`), 1763 + and Jetstream (`ws://localhost:6008/subscribe`) instead 1764 + of public infrastructure. 1765 + 1766 + AT Protocol state (PLC database, PDS data, Jetstream 1767 + cursors) is persisted to `nix/vm-data/atproto/` via 9p 1768 + mounts, so the owner DID and account data survive VM 1769 + reboots. 1770 + 1771 + In dev mode, the appview, knot, and spindle use a 1772 + `DevDirectory` identity resolver that resolves `.test` TLD 1773 + handles via the local PDS's 1774 + `com.atproto.identity.resolveHandle` endpoint rather than 1775 + DNS/HTTPS. The appview's OAuth flow also uses a relaxed 1776 + `DevStartAuthFlow` that works with HTTP endpoints. 1777 + 1778 + ### PDS admin password 1779 + 1780 + The PDS admin password is `tangled-local-dev`. This is 1781 + needed if you want to manage accounts via the PDS admin 1782 + API: 1783 + 1784 + ```bash 1785 + curl -u admin:tangled-local-dev \ 1786 + http://localhost:2583/xrpc/com.atproto.server.listAccounts 1787 + ``` 1788 + 1789 + ### Resetting state 1790 + 1791 + To wipe all persistent state and start fresh: 1792 + 1793 + ```bash 1794 + nix run .#local-dev -- --reset 1795 + ``` 1796 + 1797 + This deletes the VM disk image, all 9p-persisted data 1798 + (knot, spindle, AT Protocol state), and the appview 1799 + database. After resetting, run `nix run .#local-dev` to 1800 + bootstrap a new environment from scratch. 1801 + 1802 + You can also reset individual components manually: 1803 + 1804 + ```bash 1805 + # Reset only AT Protocol state (new DID, new account) 1806 + rm -rf nix/vm-data/atproto/* 1807 + rm -f nixos.qcow2 1808 + 1809 + # Reset everything 1810 + rm -rf nix/vm-data/{knot,spindle,atproto}/* 1811 + rm -f nixos.qcow2 appview.db* 1812 + ``` 1813 + 1625 1814 # Contribution guide 1626 1815 1627 1816 ## Commit guidelines

History

2 rounds 0 comments
sign up or login to add to the discussion
1 commit
expand
docs: add fully offline development guide
no conflicts, ready to merge
expand 0 comments
nolith.dev submitted #0
1 commit
expand
docs: add fully offline development guide
expand 0 comments