import { BeakerIcon, EyeIcon, IssueDraftIcon, PencilIcon } from '@primer/octicons-react' import { type ActionFunctionArgs, json } from '@remix-run/node' import { useLoaderData } from '@remix-run/react' import { useState } from 'react' import { Tab, TabList, TabPanel, Tabs } from 'react-aria-components' import { ClientOnly } from 'remix-utils/client-only' import Link from '~/components/Link' import Notice from '~/components/Notice' import { cn } from '~/utils/cn' import { loadAcl, loadContext, patchAcl } from '~/utils/config/headplane' import { sighupHeadscale } from '~/utils/docker' import { getSession } from '~/utils/sessions' import Editor from './editor' import Fallback from './fallback' export async function loader() { const context = await loadContext() if (!context.acl.read) { throw new Error('No ACL configuration is available') } const { data, type } = await loadAcl() return { hasAclWrite: context.acl.write, currentAcl: data, aclType: type, } } export async function action({ request }: ActionFunctionArgs) { const session = await getSession(request.headers.get('Cookie')) if (!session.has('hsApiKey')) { return json({ success: false }, { status: 401, }) } const context = await loadContext() if (!context.acl.write) { return json({ success: false }, { status: 403, }) } const data = await request.json() as { acl: string } await patchAcl(data.acl) if (context.docker) { await sighupHeadscale() } return json({ success: true }) } export default function Page() { const data = useLoaderData() const [acl, setAcl] = useState(data.currentAcl) return (
{data.hasAclWrite ? undefined : (
The ACL policy file is readonly to Headplane. You will not be able to make changes here.
)}

Access Control List (ACL)

The ACL file is used to define the access control rules for your network. You can find more information about the ACL file in the {' '} Tailscale ACL guide {' '} and the {' '} Headscale docs .

cn( 'px-4 py-2 rounded-tl-lg', 'focus:outline-none flex items-center gap-2', 'border-x border-gray-200 dark:border-gray-700', isSelected ? 'text-gray-900 dark:text-gray-100' : '', )} >

Edit file

cn( 'px-4 py-2', 'focus:outline-none flex items-center gap-2', 'border-x border-gray-200 dark:border-gray-700', isSelected ? 'text-gray-900 dark:text-gray-100' : '', )} >

Preview changes

cn( 'px-4 py-2 rounded-tr-lg', 'focus:outline-none flex items-center gap-2', 'border-x border-gray-200 dark:border-gray-700', isSelected ? 'text-gray-900 dark:text-gray-100' : '', )} >

Preview rules

}> {() => ( )} }> {() => ( )}

The Preview rules is very much still a work in progress. It is a bit complicated to implement right now but hopefully it will be available soon.

) }