Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .bumpy/fix-init-agent-docs-url.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
varlock: patch
---

fix stray backtick in local-encryption docs URL shown by varlock init --agent
118 changes: 53 additions & 65 deletions .cursor/rules/docs-location.mdc
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
---
description:
globs:
alwaysApply: false
---
---
description: Documentation Pages Location
globs: *.mdx
alwaysApply: false
---
# Documentation Pages Location

Expand Down Expand Up @@ -35,63 +31,64 @@ actions:
message: |
When creating documentation pages:

1. Always place documentation files in PACKAGES_ROOT/varlock-website/src/content/docs/:
1. Always place documentation files under `packages/varlock-website/src/content/docs/`:
```
packages/varlock-website/src/content/docs/
├── getting-started/
│ ├── installation.mdx
│ └── about-env-spec.mdx
├── guides/
│ ├── secrets.mdx
│ ├── security.mdx
│ └── ...
└── reference/
└── ...
├── guides/ # how-to guides (nested by topic in the sidebar)
├── integrations/ # frameworks, languages, platforms
├── plugins/ # secret backends
├── reference/ # CLI + schema language
└── env-spec/
```

2. Follow the directory structure:
- `getting-started/` - For introductory and setup content
- `guides/` - For how-to guides and tutorials
- `reference/` - For API reference and technical documentation
2. Top-level sections become sidebar **tabs** (Astro docs pattern), configured in
`packages/varlock-website/src/sidebar.ts` and rendered by `TabbedSidebar.astro`:
- **Getting Started** - Onboarding (keep short)
- **Guides** - How-to guides, nested into Core / Encryption and delivery / AI and agents / Ops and migration
- **Reference** - CLI, schema language, and `@env-spec`
- **Ecosystem** - Integrations and Plugins (nested catalogs)

Within a tab, nest subgroups so no level has more than ~8–10 siblings.

3. File naming:
- Use kebab-case for filenames
- Always use .mdx extension
- Make names descriptive of the content
- Always use `.mdx`
- Prefer a parent overview page plus children when a topic has independent H2 sections (CLI command groups, per-sandbox-tool, local vs remote MCP, etc.)

4. Never place documentation files:
- In the project root
- Outside the docs directory
- In any other location

5. Update astro.config.mjs:
- Every documentation page must have a corresponding entry in the sidebar navigation
- Add new pages to the appropriate section in the sidebar configuration
- For reference pages, use the autogenerate option if appropriate
- Example:
```javascript
sidebar: [
{
label: 'Getting Started',
items: [
{ label: 'Installation', slug: 'getting-started/installation' },
{ label: 'About env-spec', slug: 'getting-started/about-env-spec' },
],
},
{
label: 'Guides',
items: [
{ label: 'Secrets', slug: 'guides/secrets' },
{ label: 'Security', slug: 'guides/security' },
],
},
{
label: 'Reference',
autogenerate: { directory: 'reference' },
},
]
- Outside `packages/varlock-website/src/content/docs/`
- Under `guides/` for Docker (use `integrations/docker`)

5. Update the sidebar in `packages/varlock-website/src/sidebar.ts`:
- Every page must have a corresponding sidebar entry
- Add the page under the correct nested subgroup (do not append to a flat 15+ item list)
- Aim for ≤8–10 siblings at any nesting level
- Example (page under Guides > Core):
```ts
{
label: 'Guides',
items: [
{
label: 'Core',
collapsed: true,
items: [
{ label: 'Schema', slug: 'guides/schema' },
{ label: 'New Feature', slug: 'guides/new-feature' },
],
},
],
}
```

Top-level labels must stay one of: Getting Started, Guides, Reference, Ecosystem
(they render as the tab switcher). Do not add a fifth top-level group without
updating `TabbedSidebar.astro` icons.

6. If the page is a plugin or integration, also update homepage tiles in
`packages/varlock-website/src/lib/works-with-tiles.ts` when it should appear on the homepage.

examples:
- input: |
# Bad: Documentation file in wrong location
Expand All @@ -103,23 +100,14 @@ examples:
packages/varlock-website/src/content/docs/guides/my-guide.mdx
output: "Correctly placed documentation file"
- input: |
# Bad: Missing sidebar entry
# Created file: packages/varlock-website/src/content/docs/guides/new-feature.mdx
# But no corresponding entry in astro.config.mjs sidebar

# Good: With sidebar entry
# Created file: packages/varlock-website/src/content/docs/guides/new-feature.mdx
# Added to astro.config.mjs:
# {
# label: 'Guides',
# items: [
# { label: 'New Feature', slug: 'guides/new-feature' },
# ],
# }
output: "Documentation file with proper sidebar entry"
# Bad: Flat dump into Guides with 20 siblings
# Added guides/new-ai-thing.mdx at the end of the Guides items array

# Good: Nested under the right subgroup
# Added under Guides > AI and agents in sidebar.ts
output: "Documentation file nested in the correct sidebar subgroup"

metadata:
priority: high
version: 1.0
</rule>
version: 2.0
</rule>
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ curl -sSfL https://varlock.dev/install.sh | sh -s # via cURL
# Or use the official Docker image
docker pull ghcr.io/dmno-dev/varlock:latest
```
See the full [installation docs](https://varlock.dev/getting-started/installation/) or the [Docker guide](https://varlock.dev/guides/docker/) for more information.
See the full [installation docs](https://varlock.dev/getting-started/installation/) or the [Docker guide](https://varlock.dev/integrations/docker/) for more information.


## Workflow
Expand Down Expand Up @@ -146,8 +146,8 @@ Varlock is built on top of @env-spec, a new DSL for attaching a schema and addit
## MCP Servers
| MCP Server | Link | URL |
| --- | --- | --- |
| Varlock Docs (HTTP) | [Installation](https://varlock.dev/guides/mcp/#docs-mcp) | https://docs.mcp.varlock.dev/mcp |
| Varlock Docs (SSE) | [Installation](https://varlock.dev/guides/mcp/#docs-mcp) | https://docs.mcp.varlock.dev/sse |
| Varlock Docs (HTTP) | [Installation](https://varlock.dev/guides/mcp/docs-mcp/) | https://docs.mcp.varlock.dev/mcp |
| Varlock Docs (SSE) | [Installation](https://varlock.dev/guides/mcp/docs-mcp/) | https://docs.mcp.varlock.dev/sse |

## Examples
Examples of integrating varlock in various frameworks and situations can be found in the [Varlock examples repo](https://github.com/dmno-dev/varlock-examples)
Expand Down
15 changes: 15 additions & 0 deletions packages/varlock-website/astro.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,20 @@ import { sidebar } from './src/sidebar';
// https://astro.build/config
export default defineConfig({
site: 'https://varlock.dev',
redirects: {
'/guides/docker': '/integrations/docker/',
// sandboxing concepts moved under the proxy section, tool recipes into their own ecosystem group
'/guides/sandboxing': '/guides/proxy/sandboxing/',
'/guides/sandboxing/minimal': '/sandboxes/minimal/',
'/guides/sandboxing/fence': '/sandboxes/fence/',
'/guides/sandboxing/yolobox': '/sandboxes/yolobox/',
'/guides/sandboxing/agent-safehouse': '/sandboxes/agent-safehouse/',
'/guides/sandboxing/bubblewrap': '/sandboxes/bubblewrap/',
'/guides/sandboxing/mxc': '/sandboxes/mxc/',
// CLI / VS Code / README still link to the bare /env-spec path
'/env-spec': '/env-spec/overview/',
'/env-spec/': '/env-spec/overview/',
},
vite: {
resolve: {
alias: {
Expand Down Expand Up @@ -113,6 +127,7 @@ export default defineConfig({
ThemeProvider: '@/components/CustomStarlightThemeProvider.astro',
SocialIcons: '@/components/SocialIcons.astro',
Footer: '@/components/Footer.astro',
Sidebar: '@/components/TabbedSidebar.astro',
},
customCss: ['@/styles/global.css'],
plugins: [starlightLlmsTxt()],
Expand Down
153 changes: 145 additions & 8 deletions packages/varlock-website/src/components/CustomHead.astro
Original file line number Diff line number Diff line change
@@ -1,9 +1,146 @@
---
import Head from "@astrojs/starlight/components/Head.astro";
import { Font } from "astro:assets";
import animLogo from "../assets/logos/varlock-logo-anim.gif";
/**
* Early hash redirects for docs hubs whose sections moved to child pages.
* Emitted only on those hubs; runs synchronously in <head> before paint.
*/
import Head from '@astrojs/starlight/components/Head.astro';
import { Font } from 'astro:assets';
import animLogo from '../assets/logos/varlock-logo-anim.gif';

/** pathname without trailing slash (except root) → hash id → destination URL */
const HASH_REDIRECTS: Record<string, Record<string, string>> = {
'/guides/mcp': {
'local-mcp-servers-with-stdio': '/guides/mcp/local/',
'server-setup': '/guides/mcp/local/#server-setup',
'docker-local': '/guides/mcp/local/#docker-local',
'client-configuration': '/guides/mcp/local/#client-configuration',
'remote-mcp-servers': '/guides/mcp/remote/',
'server-implementation': '/guides/mcp/remote/#server-implementation',
'production-deployment': '/guides/mcp/remote/#production-deployment',
'docs-mcp': '/guides/mcp/docs-mcp/',
},
'/guides/proxy': {
'routing-rules': '/guides/proxy/rules/#routing-rules',
'grouping-rules-for-one-domain': '/guides/proxy/rules/#grouping-rules-for-one-domain',
'attached-vs-detached-rules': '/guides/proxy/rules/#attached-vs-detached-rules',
'egress-modes': '/guides/proxy/rules/#egress-modes',
'controlling-what-the-agent-sees': '/guides/proxy/rules/#controlling-what-the-agent-sees',
placeholders: '/guides/proxy/rules/#placeholders',
'running-modes': '/guides/proxy/running/#running-modes',
'one-shot-proxy-run': '/guides/proxy/running/#one-shot-proxy-run',
'daemon--attach-proxy-start': '/guides/proxy/running/#daemon--attach-proxy-start',
'manual-env-proxy-start--proxy-env': '/guides/proxy/running/#manual-env-proxy-start--proxy-env',
sessions: '/guides/proxy/running/#sessions',
'how-the-child-is-wired': '/guides/proxy/running/#how-the-child-is-wired',
'pinning-the-port-and-ca-location': '/guides/proxy/running/#pinning-the-port-and-ca-location',
auditing: '/guides/proxy/running/#auditing',
'editing-the-schema-while-a-session-is-running':
'/guides/proxy/running/#editing-the-schema-while-a-session-is-running',
sandboxing: '/guides/proxy/sandboxing/',
reference: '/guides/proxy/rules/#reference',
},
'/guides/proxy/sandboxing': {
minimal: '/sandboxes/minimal/',
'verify-on-your-install': '/sandboxes/minimal/#verify-on-your-install',
'claude-code-and-host-key-pinholes': '/sandboxes/minimal/#claude-code-and-host-key-pinholes',
fence: '/sandboxes/fence/',
install: '/sandboxes/fence/#install',
'wire-fence-through-varlock': '/sandboxes/fence/#wire-fence-through-varlock',
yolobox: '/sandboxes/yolobox/',
'install-1': '/sandboxes/yolobox/#install',
'launch-with-proxy-env': '/sandboxes/yolobox/#launch-with-proxy-env',
'agent-safehouse': '/sandboxes/agent-safehouse/',
'install-2': '/sandboxes/agent-safehouse/#install',
'launch-with-proxy-env-1': '/sandboxes/agent-safehouse/#launch-with-proxy-env',
bubblewrap: '/sandboxes/bubblewrap/',
'install-3': '/sandboxes/bubblewrap/#install',
'launch-with-proxy-env-2': '/sandboxes/bubblewrap/#launch-with-proxy-env',
'mxc-windows': '/sandboxes/mxc/',
'install-4': '/sandboxes/mxc/#install',
'wire-mxc-through-varlock': '/sandboxes/mxc/#wire-mxc-through-varlock',
},
'/plugins/kubernetes': {
'installation-and-setup': '/plugins/kubernetes/setup/',
'authentication-options': '/plugins/kubernetes/setup/#authentication-options',
'automatic-authentication-recommended-for-local-dev':
'/plugins/kubernetes/setup/#automatic-authentication-recommended-for-local-dev',
'explicit-cluster-server-and-token': '/plugins/kubernetes/setup/#explicit-cluster-server-and-token',
'raw-kubeconfig': '/plugins/kubernetes/setup/#raw-kubeconfig',
'multiple-instances': '/plugins/kubernetes/setup/#multiple-instances',
'loading-values': '/plugins/kubernetes/loading/',
'how-secrets-and-configmaps-are-structured':
'/plugins/kubernetes/loading/#how-secrets-and-configmaps-are-structured',
'secret-keys': '/plugins/kubernetes/loading/#secret-keys',
'configmap-keys': '/plugins/kubernetes/loading/#configmap-keys',
'default-secretconfigmap-recommended-for-the-common-case':
'/plugins/kubernetes/loading/#default-secretconfigmap-recommended-for-the-common-case',
'bulk-loading': '/plugins/kubernetes/loading/#bulk-loading',
'optional-values': '/plugins/kubernetes/loading/#optional-values',
'kubernetes-setup': '/plugins/kubernetes/cluster-setup/',
'required-rbac-permissions': '/plugins/kubernetes/cluster-setup/#required-rbac-permissions',
'service-account-for-in-cluster-use': '/plugins/kubernetes/cluster-setup/#service-account-for-in-cluster-use',
'generate-a-bearer-token-for-explicit-auth':
'/plugins/kubernetes/cluster-setup/#generate-a-bearer-token-for-explicit-auth',
'verify-access': '/plugins/kubernetes/cluster-setup/#verify-access',
reference: '/plugins/kubernetes/reference/',
'root-decorators': '/plugins/kubernetes/reference/#root-decorators',
initkubernetes: '/plugins/kubernetes/reference/#initkubernetes',
'data-types': '/plugins/kubernetes/reference/#data-types',
kubernetesbearertoken: '/plugins/kubernetes/reference/#kubernetesbearertoken',
'resolver-functions': '/plugins/kubernetes/reference/#resolver-functions',
k8ssecret: '/plugins/kubernetes/reference/#k8ssecret',
k8sconfigmap: '/plugins/kubernetes/reference/#k8sconfigmap',
k8ssecretbulk: '/plugins/kubernetes/reference/#k8ssecretbulk',
k8sconfigmapbulk: '/plugins/kubernetes/reference/#k8sconfigmapbulk',
troubleshooting: '/plugins/kubernetes/reference/#troubleshooting',
'secret-or-configmap-not-found-404': '/plugins/kubernetes/reference/#secret-or-configmap-not-found-404',
'permission-denied-403': '/plugins/kubernetes/reference/#permission-denied-403',
'authentication-failed-401': '/plugins/kubernetes/reference/#authentication-failed-401',
'connection-refused-or-tls-errors': '/plugins/kubernetes/reference/#connection-refused-or-tls-errors',
'wrong-namespace': '/plugins/kubernetes/reference/#wrong-namespace',
resources: '/plugins/kubernetes/reference/#resources',
},
'/reference/cli-commands': {
init: '/reference/cli/project/#init',
scan: '/reference/cli/project/#scan',
'install-plugin': '/reference/cli/project/#install-plugin',
telemetry: '/reference/cli/project/#telemetry',
help: '/reference/cli/project/#help',
load: '/reference/cli/load-and-run/#load',
'output-formats-for-scripts--agents':
'/reference/cli/load-and-run/#output-formats-for-scripts--agents',
run: '/reference/cli/load-and-run/#run',
printenv: '/reference/cli/load-and-run/#printenv',
explain: '/reference/cli/load-and-run/#explain',
encrypt: '/reference/cli/encryption/#encrypt',
reveal: '/reference/cli/encryption/#reveal',
lock: '/reference/cli/encryption/#lock',
audit: '/reference/cli/encryption/#audit',
'generate-key': '/reference/cli/encryption/#generate-key',
cache: '/reference/cli/cache-and-codegen/#cache',
codegen: '/reference/cli/cache-and-codegen/#codegen',
typegen: '/reference/cli/cache-and-codegen/#codegen',
proxy: '/reference/cli/proxy/#proxy',
},
};

const pathname = Astro.url.pathname.replace(/\/$/, '') || '/';
const hashRedirects = HASH_REDIRECTS[pathname] ?? null;
---

{
hashRedirects && (
<script is:inline define:vars={{ hashRedirects }}>
(() => {
const id = location.hash.slice(1);
if (!id) return;
const dest = hashRedirects[id];
if (dest) location.replace(dest);
})();
</script>
)
}

<!-- <Font cssVariable="--font-pixel" preload /> -->
<Font cssVariable="--font-jetbrains-mono" preload />
<Font cssVariable="--font-default" preload />
Expand All @@ -12,19 +149,19 @@ import animLogo from "../assets/logos/varlock-logo-anim.gif";
</Head>

<script is:inline define:vars={{ animLogoSrc: animLogo.src }}>
document.addEventListener("DOMContentLoaded", () => {
const siteTitle = document.querySelector(".site-title");
const logoImg = siteTitle?.querySelector("img");
document.addEventListener('DOMContentLoaded', () => {
const siteTitle = document.querySelector('.site-title');
const logoImg = siteTitle?.querySelector('img');
if (!logoImg) return;

const staticSrc = logoImg.src;
// preload the gif so hover swap is instant
new Image().src = animLogoSrc;

siteTitle.addEventListener("mouseenter", () => {
siteTitle.addEventListener('mouseenter', () => {
logoImg.src = animLogoSrc;
});
siteTitle.addEventListener("mouseleave", () => {
siteTitle.addEventListener('mouseleave', () => {
logoImg.src = staticSrc;
});
});
Expand Down
13 changes: 13 additions & 0 deletions packages/varlock-website/src/components/OpExamplesTip.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
/**
* Tip shown after the first `op()` example in AI Tools docs.
* Prefer any secrets plugin; examples use 1Password for concreteness.
*/
import { Aside } from '@astrojs/starlight/components';
---

<Aside type="tip">
You can use any secrets provider you want, but we like 1Password since it uses biometric authentication
for local access. All the examples in this guide use the <a href="/plugins/1password/">1Password plugin</a>
(<code>op()</code>) to show more real world examples. See that page for plugin setup and auth options.
</Aside>
Loading
Loading