Secure Playground for Vibe Coders
A first look at Minato.
Agentic coding tools like Claude Code have opened the door for a new wave of software creators. We wanted to give these users access to a secure and easy-to-use environment so that they can quickly test out ideas.
There was a pretty urgent need for creating an alternative here in order to avoid having people going full shadow IT creating personal Vercel accounts or even worse; exposing applications running on their laptops with ngrok or cloudflared. So in this post we want to share our experience with creating a prototype for a "vibe coding platform".
Instead of giving users a full Kubernetes cluster with cluster-admin, something that Claude Code is actually very good at working with, we instead wanted to give an experience similar to the public cloud PaaS that is available today. There's also a lot of overhead, both resource- and management-wise, when giving everyone their own cluster.
If we wanted to copy Vercel or Fly.io we could fairly easily do that (bold statement), but the freedom you get there can often come at the cost of security. We wanted to make something where you cannot make these mistakes no matter how you tried.
So when we started dabbling with this we were always thinking; "how much can we simplify this", "how much can we hide from the user", "how many security constraints can we add without anyone noticing".
Our internal prototype that was made in about a week is called "Minato", which is Japanese for "harbor" or "port". This is simply us trying to make it fit the maritime theme of the Kubernetes and Cloud Native landscape.
Workflow
Users navigate to the Minato website and are presented with the choice of two different templates: "React SPA" or "React + Go + Postgres".
We also added an option for those who just need to publish a static HTML page, more on this later.
Clicking on either of these opens the Github "New repository" page with the template selected.
After repo creation, a template init action will register it as an application in our infrastructure repository:
# apps/my-app.yaml
name: my-app
repoURL: https://github.com/intility/my-app.git
targetRevision: main
appPath: deploy/baseWe use this together with an Argo CD appset so that Kubernetes manifests for the newly created Minato app are automatically applied:
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: platform-apps
namespace: argocd
spec:
goTemplate: true
goTemplateOptions:
- missingkey=error
generators:
- git:
repoURL: https://github.com/intility/infra.git
revision: HEAD
files:
- path: apps/*.yaml
template:
metadata:
name: "{{ .name }}"
namespace: argocd
spec:
project: minato-tenants
source:
repoURL: "{{ .repoURL }}"
targetRevision: "{{ .targetRevision }}"
path: "{{ .appPath }}"
destination:
server: https://kubernetes.default.svc
namespace: "app-{{ .name }}"
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
- ServerSideApply=trueWhen template init is complete (takes a few seconds), the repository is ready and the application is already running in a Kubernetes cluster.
At first with a placeholder image indicating that the main application is still building (this takes a minute or so to complete).
In the repo, users are presented with a simplified README with links to the running app itself, in addition to traces, dashboards and a hosted development environment (more on this below).
After the template image is built, you can navigate to the actual running site with some status indicators:
From here on the user is free to create whatever they want by cloning the repo and pushing changes.
Examples
Here are some examples of what Intility users have created so far:
rsshub - Manages RSS feeds in a web interface and routes items to matching Slack channels
idw-jwt - An internal jwt token decoder like jwt.io
chesscapades - A web-based chess game that notifies players when it is their turn
minato-status - Displays per-app cluster status and container image verification details of Minato applications
Static HTML
Having Claude or ChatGPT produce HTML is becoming increasingly popular and often times what the user really needs is just the ability to publish this HTML so that teammates can easily view them.
We started noticing that people were sending .html files over Teams where you have to download the file and manually open it in a browser for it to render properly.
What we ended up with is a very simple site where you can upload an .html file.
After uploading you would end up with a link like https://static.example.com/p/foo that can be shared easily.
The site itself is of course a Minato application.
If you are using Github and work with public repositories or gists, you can achieve pretty much the same thing by using gistpreview / gisthost.
Fun fact: Since the time we launched Minato Static internally, similar solutions have been launced by Cloudflare, Anthropic and OpenAI.
Development Workspace
Every Minato app comes with its own development environment that opens straight in the browser.
From the repo README.md you are one click away from a full VS Code session, already pointed at the project and with Go, Node, Postgres and the Claude Code pre-installed.
It's all backed by a small go service that provisions each workspace as plain Kubernetes resources, running on Intility's own platform.
Its also safer. Running Claude Code on your own machine gives it access to everything there, but a workspace only has the project in it, so the worst case is you delete it and start again.
Security
So now that we've made it easy to deploy software, how do we keep it secure?
Here are a few of the measures taken so far:
- Deployed applications are only available on the internal network.
- All workloads have strict default deny network policies.
- A Tetragon policy kills pods that try to shell exec.
- Kyverno policies only allow approved image registries and enforce digest pinning of images.
- All images are signed with cosign when built.
- Image signatures are verified clusterside with sigstore policy controller.
- Pods with critical vulnerabilities are evicted after a grace period.
- Github templates come with strict github action checks.
- Templates themselves have sane defaults and give a secure starting point.
- Automatic dependabot updates.
- Forwarding of pod logs.
We've had the freedom to experiment with different techniques here, which has been fun, and most of these seem to have gone more or less unnoticed by the users.
There's probably a lot more that can be done here, and scenarios that we haven't thought of yet, but it's a start.
Next steps
What we have running right now runs in one cluster tied to a specific tenant. This works well, but as mentioned, requiring every tenant to have their own cluster adds significant overhead. Therefore the next version of Minato, which will be the one we actually provide as a service, will be completely multi-tenant and delivered from a single cluster (and possibly later across a shared pool of clusters).
The landscape changes fast these days, so stay tuned for the next iteration.
if (wantUpdates == true) {followIntilityOnLinkedIn();}