Skip to content

Commit 5c85ff8

Browse files
authored
Merge pull request #262 from aojea/gotobeta
Gotobeta
2 parents c95ebaf + ca78dd2 commit 5c85ff8

15 files changed

Lines changed: 400 additions & 23 deletions

File tree

.github/workflows/govulncheck.yml

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
name: Govulncheck
2+
3+
on:
4+
push:
5+
branches:
6+
- 'main'
7+
tags:
8+
- 'v*'
9+
pull_request:
10+
branches: [ main ]
11+
schedule:
12+
- cron: '0 6 * * 1'
13+
14+
permissions:
15+
contents: read
16+
17+
env:
18+
GO_VERSION: "1.26"
19+
20+
jobs:
21+
govulncheck:
22+
runs-on: ubuntu-latest
23+
steps:
24+
- id: govulncheck
25+
continue-on-error: true
26+
uses: golang/govulncheck-action@032d45514ae346b1db93c04b0c90b841c370344f # v1.1.0
27+
with:
28+
go-version-input: ${{ env.GO_VERSION }}
29+
go-package: ./...
30+
output-file: govulncheck.txt
31+
- name: Fail only on vulnerabilities we haven't explicitly accepted
32+
env:
33+
GOVULNCHECK_OUTCOME: ${{ steps.govulncheck.outcome }}
34+
run: |
35+
set -euo pipefail
36+
cat govulncheck.txt
37+
38+
# GO-2024-3218 (CVE-2023-26248): content-censorship risk inherent to
39+
# Kademlia DHT routing in github.com/libp2p/go-libp2p-kad-dht.
40+
# Upstream advisory lists "no known fixed" version, since it's a
41+
# structural property of Kademlia DHTs, not a patchable bug. SAM's
42+
# mesh transport depends fundamentally on this module.
43+
# See https://pkg.go.dev/vuln/GO-2024-3218. Re-evaluate if this ever
44+
# gets a fixed version upstream.
45+
accepted=("GO-2024-3218")
46+
47+
found=$(grep -oE '^Vulnerability #[0-9]+: GO-[0-9]{4}-[0-9]+' govulncheck.txt | awk '{print $3}' | sort -u || true)
48+
49+
unaccepted=""
50+
for id in $found; do
51+
skip=false
52+
for a in "${accepted[@]}"; do
53+
[ "$a" = "$id" ] && skip=true && break
54+
done
55+
if [ "$skip" = false ]; then
56+
unaccepted="$unaccepted $id"
57+
fi
58+
done
59+
60+
if [ -n "$unaccepted" ]; then
61+
echo "::error::New/unaccepted vulnerabilities found:$unaccepted"
62+
exit 1
63+
fi
64+
65+
if [ "$GOVULNCHECK_OUTCOME" = "failure" ] && [ -z "$found" ]; then
66+
echo "::error::govulncheck failed for a reason other than a detected vulnerability; see output above."
67+
exit 1
68+
fi
69+
70+
echo "OK - vulnerabilities found (all accepted): ${found:-none}"

.github/workflows/release.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,8 @@ jobs:
5151
env:
5252
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
5353
- name: Upload Mobile App to Release
54-
uses: softprops/action-gh-release@3d0d9888cb7fd7b750713d6e236d1fcb99157228 # v3.0.2
55-
with:
56-
files: mobile/sam-node-app/build/app/outputs/flutter-apk/app-release.apk
5754
env:
5855
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
56+
GH_REPO: ${{ github.repository }}
57+
TAG_NAME: ${{ github.ref_name }}
58+
run: gh release upload "$TAG_NAME" mobile/sam-node-app/build/app/outputs/flutter-apk/app-release.apk --clobber

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ Get a node running on the public testnet (`bananas.sam-mesh.dev`) in minutes:
3030
- 🚀 **[User Quick Start Guide](site/content/docs/quickstart.md)**: Connect and run a SAM node using binaries or Docker, and query the local MCP server.
3131
- 🤖 **[Agent Integration Guides](site/content/docs/integrations/_index.md)**: Connect Google Gemini, Claude, and other AI agents to your SAM node to dynamically discover and call tools across the mesh.
3232
- 📡 **[Testnet Validation Tutorial](site/content/docs/development/testnet-validation.md)**: Real-time verification, remote tool invocation, and HTTP stream proxies.
33+
- 🎛️ **[Production Kubernetes Deployment](site/content/docs/user/kubernetes-deployment.md)**: Run your own hub (control plane, router, console) via plain manifests, or via the [sam-mesh Helm chart](charts/sam-mesh/README.md) for local/self-hosted setups.
3334

3435
### For Developers & Contributors
3536
Compile from source, run local clusters, or execute tests:

charts/sam-mesh/README.md

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
# sam-mesh Helm chart
2+
3+
Deploys a self-contained SAM mesh (control plane, router, console, and
4+
optionally an in-cluster Postgres + Dex) for local development, testing, or
5+
self-hosting your own hub.
6+
7+
> For large-scale production deployments (GKE/EKS/AKS) using externally
8+
> managed Postgres/DNS/OIDC, see the
9+
> [Production Kubernetes Deployment guide](https://sam-mesh.dev/docs/user/kubernetes-deployment/),
10+
> which uses plain manifests instead of this chart.
11+
12+
## Install
13+
14+
```bash
15+
helm upgrade --install sam-mesh ./charts/sam-mesh --namespace sam --create-namespace
16+
```
17+
18+
At the end of `helm install`/`helm upgrade`, the chart prints the exact
19+
`kubectl` command to retrieve your generated secrets (see below) — read the
20+
NOTES output before doing anything else.
21+
22+
## Secrets: `controlPlane.adminToken` and `database.postgres.password`
23+
24+
Both default to `""` in [values.yaml](values.yaml). When left blank, the chart
25+
**auto-generates** a random 32-character secret on first install and stores it
26+
in the `<release>-secrets` Kubernetes Secret; the same value is reused on
27+
`helm upgrade` (it is not rotated on every upgrade). Retrieve the admin token
28+
with:
29+
30+
```bash
31+
kubectl get secret --namespace <namespace> <release>-secrets -o jsonpath='{.data.admin-token}' | base64 -d; echo
32+
```
33+
34+
You can also pin either value explicitly instead of letting the chart
35+
generate one, e.g. for reproducible dev environments or to match an
36+
existing secret:
37+
38+
```bash
39+
helm upgrade --install sam-mesh ./charts/sam-mesh \
40+
--set controlPlane.adminToken="$(openssl rand -hex 32)" \
41+
--set database.postgres.password="$(openssl rand -hex 32)"
42+
```
43+
44+
## `controlPlane.insecureSkipTlsVerify`
45+
46+
Defaults to `false`. Only set this to `true` when `controlPlane.oidcIssuer`
47+
points at an OIDC issuer served with a self-signed or otherwise untrusted
48+
certificate — for example the Kubernetes API server's own issuer
49+
(`https://kubernetes.default.svc.cluster.local`) used for ServiceAccount
50+
Workload Identity Federation in local `kind` clusters, or a local Dex/mock
51+
OIDC instance without a real cert. Leave it `false` for any real-world OIDC
52+
provider (Google, Okta, Dex behind a real TLS certificate, etc.).
53+
54+
## `controlPlane.autoApproveEnrollment`
55+
56+
Defaults to `true` (any node/router presenting a valid identity token is
57+
enrolled immediately, no manual step). Set to `false` if you want an
58+
administrator to approve each enrollment via `/admin/enrollments` before a
59+
node can join — see the
60+
[Control Plane Configuration guide](https://sam-mesh.dev/docs/user/control-plane-configuration/#6-headless-node-enrollment-bootstrap-token-flow).
61+
62+
## Dex (`dex.enabled`)
63+
64+
Disabled by default. The bundled Dex is only meant for local/dev OIDC login
65+
(username/password test users); real deployments should point
66+
`controlPlane.oidcIssuer` at your own identity provider instead of enabling
67+
this.
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
SAM Mesh has been deployed as release "{{ .Release.Name }}" in namespace "{{ .Release.Namespace }}".
2+
3+
{{- if not .Values.controlPlane.adminToken }}
4+
5+
A random admin token was generated for you (nothing is set in values.yaml).
6+
Retrieve it before you try to call any /admin/* endpoint or log into the
7+
console as root admin:
8+
9+
kubectl get secret --namespace {{ .Release.Namespace }} {{ include "sam-mesh.fullname" . }}-secrets -o jsonpath='{.data.admin-token}' | base64 -d; echo
10+
{{- else }}
11+
12+
Using the admin token you provided via values (controlPlane.adminToken).
13+
{{- end }}
14+
15+
{{- if not .Values.database.postgres.password }}
16+
17+
A random database password was also generated and stored in the same secret
18+
(key "db-password"); you shouldn't need it directly unless connecting to
19+
Postgres yourself.
20+
{{- end }}
21+
22+
Control plane service: {{ include "sam-mesh.fullname" . }}-control-plane:{{ .Values.controlPlane.service.port }}
23+
{{- if .Values.console.enabled }}
24+
Console service: {{ include "sam-mesh.fullname" . }}-console:{{ .Values.console.service.port }}
25+
{{- end }}
26+
27+
{{- if .Values.controlPlane.autoApproveEnrollment }}
28+
29+
Note: controlPlane.autoApproveEnrollment=true - any node/router presenting a
30+
valid identity is enrolled automatically without manual admin approval. Set
31+
it to false for production deployments where you want to review enrollments.
32+
{{- end }}
33+
34+
See charts/sam-mesh/README.md for more details on configuring OIDC, TLS, and
35+
production values.
Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,28 @@
1+
{{- $secretName := printf "%s-secrets" (include "sam-mesh.fullname" .) }}
2+
{{- $existing := lookup "v1" "Secret" .Release.Namespace $secretName }}
3+
{{- $adminToken := .Values.controlPlane.adminToken }}
4+
{{- if not $adminToken }}
5+
{{- if and $existing (hasKey $existing "data") (hasKey $existing.data "admin-token") }}
6+
{{- $adminToken = index $existing.data "admin-token" | b64dec }}
7+
{{- else }}
8+
{{- $adminToken = randAlphaNum 32 }}
9+
{{- end }}
10+
{{- end }}
11+
{{- $dbPassword := .Values.database.postgres.password }}
12+
{{- if not $dbPassword }}
13+
{{- if and $existing (hasKey $existing "data") (hasKey $existing.data "db-password") }}
14+
{{- $dbPassword = index $existing.data "db-password" | b64dec }}
15+
{{- else }}
16+
{{- $dbPassword = randAlphaNum 32 }}
17+
{{- end }}
18+
{{- end }}
119
apiVersion: v1
220
kind: Secret
321
metadata:
4-
name: {{ include "sam-mesh.fullname" . }}-secrets
22+
name: {{ $secretName }}
523
labels:
624
{{- include "sam-mesh.labels" . | nindent 4 }}
725
type: Opaque
826
data:
9-
admin-token: {{ .Values.controlPlane.adminToken | b64enc | quote }}
10-
db-password: {{ .Values.database.postgres.password | b64enc | quote }}
27+
admin-token: {{ $adminToken | b64enc | quote }}
28+
db-password: {{ $dbPassword | b64enc | quote }}

charts/sam-mesh/values.yaml

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,13 @@ controlPlane:
77
image:
88
repository: sam-control-plane
99
logLevel: info
10-
adminToken: super-secret-admin-token
10+
# Leave empty to auto-generate a random admin token on first install (kept
11+
# stable across upgrades). Set explicitly (e.g. via --set) to pin a known value.
12+
adminToken: ""
1113
autoApproveEnrollment: true
12-
insecureSkipTlsVerify: true
14+
# Only skips TLS verification for the OIDC issuer; leave false unless
15+
# pointing at a local/dev issuer without a valid certificate.
16+
insecureSkipTlsVerify: false
1317
oidcIssuer: "http://sam-mesh-dex:5556/dex"
1418
allowedAudiences: "sam-mesh-audience,sam-control-plane-audience"
1519
resources:
@@ -32,7 +36,9 @@ database:
3236
repository: postgres
3337
tag: 16-alpine
3438
user: sam
35-
password: sam-secret-password
39+
# Leave empty to auto-generate a random password on first install (kept
40+
# stable across upgrades). Set explicitly (e.g. via --set) to pin a known value.
41+
password: ""
3642
database: sam_mesh
3743
port: 5432
3844
sslmode: disable

cmd/sam-console/public/app.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,26 @@ document.addEventListener('DOMContentLoaded', () => {
1717

1818
// Optionally fetch data specifically for that view if needed
1919
// Currently, loadData() fetches everything from the status endpoint.
20+
21+
// On mobile the sidebar is an overlay drawer; close it after navigating.
22+
closeSidebar();
2023
});
2124
});
2225

2326
// Check auth status on load
2427
checkAuthAndLoad();
2528
});
2629

30+
window.toggleSidebar = function() {
31+
document.querySelector('.sidebar').classList.toggle('open');
32+
document.getElementById('sidebar-scrim').classList.toggle('active');
33+
};
34+
35+
window.closeSidebar = function() {
36+
document.querySelector('.sidebar').classList.remove('open');
37+
document.getElementById('sidebar-scrim').classList.remove('active');
38+
};
39+
2740
async function checkAuthAndLoad() {
2841
try {
2942
const infoResp = await fetch('info');
@@ -456,12 +469,14 @@ window.generateBootstrapToken = async function() {
456469
const role = document.getElementById('token-role').value;
457470
const owner_id = document.getElementById('token-owner').value;
458471
const max_usages = parseInt(document.getElementById('token-usages').value, 10);
472+
const ttl_hours = parseInt(document.getElementById('token-ttl').value, 10) || 24;
459473
const description = document.getElementById('token-desc').value;
460474

461475
const payload = {
462476
role,
463477
owner_id,
464478
max_usages,
479+
ttl_hours,
465480
description
466481
};
467482

0 commit comments

Comments
 (0)