PageSourceSearch

https://mstone.ai/docs/assets/js/8c0674e9.a17b99fa.js

js mstone.ai collected 2026-10-04 10:01:53 UTC 14,874 bytes, 1 lines download raw bytes

1"use strict";(globalThis.webpackChunknew_docs_mstone_ai=globalThis.webpackChunknew_docs_mstone_ai||[]).push([[4289],{3222(e,t,r){r.r(t),r.d(t,{assets:()=>d,contentTitle:()=>c,default:()=>p,frontMatter:()=>i,metadata:()=>n,toc:()=>u});const n=JSON.parse('{"id":"integration-instructions/gen-ai-coding-tools/Claude-Code/claude-code-via-relay-server","title":"Claude Code - Open Telemetry - via Relay Server","description":"Deploy an internal relay server that forwards telemetry to Milestone. Choose the deployment model that fits your infrastructure.","source":"@site/docs/integration-instructions/gen-ai-coding-tools/Claude-Code/claude-code-via-relay-server.md","sourceDirName":"integration-instructions/gen-ai-coding-tools/Claude-Code","slug":"/integration-instructions/gen-ai-coding-tools/Claude-Code/claude-code-via-relay-server","permalink":"/docs/integration-instructions/gen-ai-coding-tools/Claude-Code/claude-code-via-relay-server","draft":false,"unlisted":false,"tags":[],"version":"current","sidebarPosition":3,"frontMatter":{"title":"Claude Code - Open Telemetry - via Relay Server","sidebar_label":"Claude Code - Open Telemetry - via Relay Server","sidebar_position":3},"sidebar":"tutorialSidebar","previous":{"title":"Claude Code - Open Telemetry - Direct Setup","permalink":"/docs/integration-instructions/gen-ai-coding-tools/Claude-Code/claude-code-direct-setup"},"next":{"title":"Claude Code - Open Telemetry - via your Otel Collector","permalink":"/docs/integration-instructions/gen-ai-coding-tools/Claude-Code/claude-code-via-otel-collector"}}');var l=r(4848),a=r(8453),o=r(9489),s=r(7227);const i={title:"Claude Code - Open Telemetry - via Relay Server",sidebar_label:"Claude Code - Open Telemetry - via Relay Server",sidebar_position:3},c="Claude Code \xb7 via Relay Server",d={},u=[{value:"Server Setup (Kubernetes)",id:"server-setup-kubernetes",level:2},{value:"Step 1: Add the upstream OTel Helm repo",id:"step-1-add-the-upstream-otel-helm-repo",level:3},{value:"Step 2: Download configuration",id:"step-2-download-configuration",level:3},{value:"Step 3: Create namespace &amp; apply the Secret",id:"step-3-create-namespace--apply-the-secret",level:3},{value:"Step 4: Install the chart",id:"step-4-install-the-chart",level:3},{value:"Developer Setup",id:"developer-setup",level:2},{value:"Set User Identity",id:"set-user-identity",level:3},{value:"Restart Claude Code",id:"restart-claude-code",level:3},{value:"Server Setup (VM)",id:"server-setup-vm",level:2},{value:"Step 1: Download the tarball",id:"step-1-download-the-tarball",level:3},{value:"Step 2: Extract on the Linux host",id:"step-2-extract-on-the-linux-host",level:3},{value:"Step 3: Configure credentials",id:"step-3-configure-credentials",level:3},{value:"Step 4: Start the collector",id:"step-4-start-the-collector",level:3},{value:"Developer Setup",id:"developer-setup-1",level:2},{value:"Set User Identity",id:"set-user-identity-1",level:3},{value:"Restart Claude Code",id:"restart-claude-code-1",level:3}];function h(e){const t={code:"code",h1:"h1",h2:"h2",h3:"h3",header:"header",p:"p",pre:"pre",...(0,a.R)(),...e.components};return(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(t.header,{children:(0,l.jsx)(t.h1,{id:"claude-code--via-relay-server",children:"Claude Code \xb7 via Relay Server"})}),"\n",(0,l.jsx)(t.p,{children:"Deploy an internal relay server that forwards telemetry to Milestone. Choose the deployment model that fits your infrastructure."}),"\n","\n",(0,l.jsxs)(o.A,{children:[(0,l.jsxs)(s.A,{value:"kubernetes",label:"Kubernetes (Helm)",default:!0,children:[(0,l.jsx)(t.h2,{id:"server-setup-kubernetes",children:"Server Setup (Kubernetes)"}),(0,l.jsxs)(t.p,{children:["Install the upstream OpenTelemetry Collector chart on your Kubernetes cluster. The chart is community-maintained; Milestone provides the per-tenant ",(0,l.jsx)(t.code,{children:"values.yaml"})," and a kubectl-applyable Secret manifest."]}),(0,l.jsx)(t.h3,{id:"step-1-add-the-upstream-otel-helm-repo",children:"Step 1: Add the upstream OTel Helm repo"}),(0,l.jsx)(t.pre,{children:(0,l.jsx)(t.code,{className:"language-bash",children:"helm repo add open-telemetry https://open-telemetry.github.io/opentelemetry-helm-charts && helm repo update\n"})}),(0,l.jsx)(t.h3,{id:"step-2-download-configuration",children:"Step 2: Download configuration"}),(0,l.jsxs)(t.p,{children:["Download ",(0,l.jsx)(t.code,{children:"values.yaml"})," and ",(0,l.jsx)(t.code,{children:"secret.yaml"})," from the Integration page in the Milestone app."]}),(0,l.jsx)(t.h3,{id:"step-3-create-namespace--apply-the-secret",children:"Step 3: Create namespace & apply the Secret"}),(0,l.jsx)(t.pre,{children:(0,l.jsx)(t.code,{className:"language-bash",children:"kubectl create namespace monitoring --dry-run=client -o yaml | kubectl apply -f -\nkubectl apply -f secret.yaml -n monitoring\n"})}),(0,l.jsx)(t.h3,{id:"step-4-install-the-chart",children:"Step 4: Install the chart"}),(0,l.jsx)(t.pre,{children:(0,l.jsx)(t.code,{className:"language-bash",children:"helm install milestone-relay open-telemetry/opentelemetry-collector --version 0.156.2 -n monitoring --create-namespace -f values.yaml\n"})}),(0,l.jsx)(t.h2,{id:"developer-setup",children:"Developer Setup"}),(0,l.jsxs)(t.p,{children:["Save to ",(0,l.jsx)(t.code,{children:"~/.claude/settings.json"})," on each developer machine \u2014 replace ",(0,l.jsx)(t.code,{children:"<your-collector-endpoint>"})," with the address you exposed the collector on (internal LB, NodePort, Ingress, etc.). The service is deployed as ClusterIP \u2014 expose it however fits your network."]}),(0,l.jsx)(t.pre,{children:(0,l.jsx)(t.code,{className:"language-json",children:'{\n  "env": {\n    "CLAUDE_CODE_ENABLE_TELEMETRY": "1",\n    "OTEL_METRICS_EXPORTER": "otlp",\n    "OTEL_LOGS_EXPORTER": "otlp",\n    "OTEL_LOG_TOOL_DETAILS": "1",\n    "OTEL_EXPORTER_OTLP_PROTOCOL": "http/protobuf",\n    "OTEL_EXPORTER_OTLP_ENDPOINT": "http://<your-collector-endpoint>:4318",\n    "OTEL_EXPORTER_OTLP_HEADERS": ""\n  }\n}\n'})}),(0,l.jsx)(t.h3,{id:"set-user-identity",children:"Set User Identity"}),(0,l.jsxs)(t.p,{children:["Add to your shell profile (",(0,l.jsx)(t.code,{children:"~/.zshrc"})," or ",(0,l.jsx)(t.code,{children:"~/.bashrc"}),"):"]}),(0,l.jsx)(t.pre,{children:(0,l.jsx)(t.code,{className:"language-bash",children:'export OTEL_RESOURCE_ATTRIBUTES="user.login=$(whoami),user.email=$(whoami)@<your-company-domain>.com"\n'})}),(0,l.jsxs)(t.p,{children:["Then reload: ",(0,l.jsx)(t.code,{children:"source ~/.zshrc"})]}),(0,l.jsx)(t.h3,{id:"restart-claude-code",children:"Restart Claude Code"}),(0,l.jsx)(t.p,{children:"Close and reopen Claude Code for settings to take effect."})]}),(0,l.jsxs)(s.A,{value:"vm",label:"VM Setup",children:[(0,l.jsx)(t.h2,{id:"server-setup-vm",children:"Server Setup (VM)"}),(0,l.jsxs)(t.p,{children:["Run the OpenTelemetry Collector as a Docker container on a shared Linux host. The tarball ships with a docker-compose setup pre-wired to forward to Milestone \u2014 fill in the endpoint + secret in ",(0,l.jsx)(t.code,{children:".env"}),"."]}),(0,l.jsx)(t.h3,{id:"step-1-download-the-tarball",children:"Step 1: Download the tarball"}),(0,l.jsxs)(t.p,{children:["Download ",(0,l.jsx)(t.code,{children:"otel-client-claude-code.tar.gz"})," from the Integration page in the Milestone app."]}),(0,l.jsx)(t.h3,{id:"step-2-extract-on-the-linux-host",children:"Step 2: Extract on the Linux host"}),(0,l.jsx)(t.pre,{children:(0,l.jsx)(t.code,{className:"language-bash",children:"tar -xvzf otel-client-claude-code.tar.gz && cd client\n"})}),(0,l.jsx)(t.h3,{id:"step-3-configure-credentials",children:"Step 3: Configure credentials"}),(0,l.jsxs)(t.p,{children:["Set ",(0,l.jsx)(t.code,{children:"OTEL_SERVER_ENDPOINT"})," to ",(0,l.jsx)(t.code,{children:"https://telemetry.mstone.ai"})," and ",(0,l.jsx)(t.code,{children:"OTEL_SHARED_SECRET"})," in ",(0,l.jsx)(t.code,{children:".env"}),". Optional: pin ",(0,l.jsx)(t.code,{children:"OTEL_VERSION"})," / ",(0,l.jsx)(t.code,{children:"OTEL_IMAGE"})," to choose a specific upstream collector release."]}),(0,l.jsx)(t.pre,{children:(0,l.jsx)(t.code,{className:"language-bash",children:"cp env.example .env && nano .env\n"})}),(0,l.jsx)(t.h3,{id:"step-4-start-the-collector",children:"Step 4: Start the collector"}),(0,l.jsx)(t.pre,{children:(0,l.jsx)(t.code,{className:"language-bash",children:"docker 
1compose up -d\n"})}),(0,l.jsx)(t.h2,{id:"developer-setup-1",children:"Developer Setup"}),(0,l.jsxs)(t.p,{children:["Save to ",(0,l.jsx)(t.code,{children:"~/.claude/settings.json"})," on each developer machine \u2014 replace ",(0,l.jsx)(t.code,{children:"your-linux-server-ip"})," with the hostname or IP of the collector host:"]}),(0,l.jsx)(t.pre,{children:(0,l.jsx)(t.code,{className:"language-json",children:'{\n  "env": {\n    "CLAUDE_CODE_ENABLE_TELEMETRY": "1",\n    "OTEL_METRICS_EXPORTER": "otlp",\n    "OTEL_LOGS_EXPORTER": "otlp",\n    "OTEL_LOG_TOOL_DETAILS": "1",\n    "OTEL_EXPORTER_OTLP_PROTOCOL": "http/protobuf",\n    "OTEL_EXPORTER_OTLP_ENDPOINT": "http://your-linux-server-ip:4318",\n    "OTEL_EXPORTER_OTLP_HEADERS": ""\n  }\n}\n'})}),(0,l.jsx)(t.h3,{id:"set-user-identity-1",children:"Set User Identity"}),(0,l.jsxs)(t.p,{children:["Add to your shell profile (",(0,l.jsx)(t.code,{children:"~/.zshrc"})," or ",(0,l.jsx)(t.code,{children:"~/.bashrc"}),"):"]}),(0,l.jsx)(t.pre,{children:(0,l.jsx)(t.code,{className:"language-bash",children:'export OTEL_RESOURCE_ATTRIBUTES="user.login=$(whoami),user.email=$(whoami)@<your-company-domain>.com"\n'})}),(0,l.jsxs)(t.p,{children:["Then reload: ",(0,l.jsx)(t.code,{children:"source ~/.zshrc"})]}),(0,l.jsx)(t.h3,{id:"restart-claude-code-1",children:"Restart Claude Code"}),(0,l.jsx)(t.p,{children:"Close and reopen Claude Code for settings to take effect."})]})]})]})}function p(e={}){const{wrapper:t}={...(0,a.R)(),...e.components};return t?(0,l.jsx)(t,{...e,children:(0,l.jsx)(h,{...e})}):h(e)}},7227(e,t,r){r.d(t,{A:()=>o});r(6540);var n=r(4164);const l="tabItem_Ymn6";var a=r(4848);function o({children:e,hidden:t,className:r}){return(0,a.jsx)("div",{role:"tabpanel",className:(0,n.A)(l,r),hidden:t,children:e})}},9489(e,t,r){r.d(t,{A:()=>C});var n=r(6540),l=r(4164),a=r(8630),o=r(4245),s=r(6347),i=r(6494),c=r(2814),d=r(5167),u=r(9900);function h(e){return n.Children.toArray(e).filter(e=>"\n"!==e).map(e=>{if(!e||(0,n.isValidElement)(e)&&function(e){const{props:t}=e;return!!t&&"object"==typeof t&&"value"in t}(e))return e;throw new Error(`Docusaurus error: Bad <Tabs> child <${"string"==typeof e.type?e.type:e.type.name}>: all children of the <Tabs> component should be <TabItem>, and every <TabItem> should have a unique "value" prop.`)})?.filter(Boolean)??[]}function p(e){const{values:t,children:r}=e;return(0,n.useMemo)(()=>{const e=t??function(e){return h(e).map(({props:{value:e,label:t,attributes:r,default:n}})=>({value:e,label:t,attributes:r,default:n}))}(r);return function(e){const t=(0,d.XI)(e,(e,t)=>e.value===t.value);if(t.length>0)throw new Error(`Docusaurus error: Duplicate values "${t.map(e=>e.value).join(", ")}" found in <Tabs>. Every value needs to be unique.`)}(e),e},[t,r])}function m({value:e,tabValues:t}){return t.some(t=>t.value===e)}function v({queryString:e=!1,groupId:t}){const r=(0,s.W6)(),l=function({queryString:e=!1,groupId:t}){if("string"==typeof e)return e;if(!1===e)return null;if(!0===e&&!t)throw new Error('Docusaurus error: The <Tabs> component groupId prop is required if queryString=true, because this value is used as the search param name. You can also provide an explicit value such as queryString="my-search-param".');return t??null}({queryString:e,groupId:t});return[(0,c.aZ)(l),(0,n.useCallback)(e=>{if(!l)return;const t=new URLSearchParams(r.location.search);t.set(l,e),r.replace({...r.location,search:t.toString()})},[l,r])]}function x(e){const{defaultValue:t,queryString:r=!1,groupId:l}=e,a=p(e),[o,s]=(0,n.useState)(()=>function({defaultValue:e,tabValues:t}){if(0===t.length)throw new Error("Docusaurus error: the <Tabs> component requires at least one <TabItem> children component");if(e){if(!m({value:e,tabValues:t}))throw new Error(`Docusaurus error: The <Tabs> has a defaultValue "${e}" but none of its children has the corresponding value. Available values are: ${t.map(e=>e.value).join(", ")}. If you intend to show no default tab, use defaultValue={null} instead.`);return e}const r=t.find(e=>e.default)??t[0];if(!r)throw new Error("Unexpected error: 0 tabValues");return r.value}({defaultValue:t,tabValues:a})),[c,d]=v({queryString:r,groupId:l}),[h,x]=function({groupId:e}){const t=function(e){return e?`docusaurus.tab.${e}`:null}(e),[r,l]=(0,u.Dv)(t);return[r,(0,n.useCallback)(e=>{t&&l.set(e)},[t,l])]}({groupId:l}),f=(()=>{const e=c??h;return m({value:e,tabValues:a})?e:null})();(0,i.A)(()=>{f&&s(f)},[f]);return{selectedValue:o,selectValue:(0,n.useCallback)(e=>{if(!m({value:e,tabValues:a}))throw new Error(`Can't select invalid tab value=${e}`);s(e),d(e),x(e)},[d,x,a]),tabValues:a}}var f=r(1062);const y="tabList__CuJ",b="tabItem_LNqP";var g=r(4848);function j({className:e,block:t,selectedValue:r,selectValue:n,tabValues:a}){const s=[],{blockElementScrollPositionUntilNextRender:i}=(0,o.a_)(),c=e=>{const t=e.currentTarget,l=s.indexOf(t),o=a[l].value;
1o!==r&&(i(t),n(o))},d=e=>{let t=null;switch(e.key){case"Enter":c(e);break;case"ArrowRight":{const r=s.indexOf(e.currentTarget)+1;t=s[r]??s[0];break}case"ArrowLeft":{const r=s.indexOf(e.currentTarget)-1;t=s[r]??s[s.length-1];break}}t?.focus()};return(0,g.jsx)("ul",{role:"tablist","aria-orientation":"horizontal",className:(0,l.A)("tabs",{"tabs--block":t},e),children:a.map(({value:e,label:t,attributes:n})=>(0,g.jsx)("li",{role:"tab",tabIndex:r===e?0:-1,"aria-selected":r===e,ref:e=>{s.push(e)},onKeyDown:d,onClick:c,...n,className:(0,l.A)("tabs__item",b,n?.className,{"tabs__item--active":r===e}),children:t??e},e))})}function E({lazy:e,children:t,selectedValue:r}){const a=(Array.isArray(t)?t:[t]).filter(Boolean);if(e){const e=a.find(e=>e.props.value===r);return e?(0,n.cloneElement)(e,{className:(0,l.A)("margin-top--md",e.props.className)}):null}return(0,g.jsx)("div",{className:"margin-top--md",children:a.map((e,t)=>(0,n.cloneElement)(e,{key:t,hidden:e.props.value!==r}))})}function T(e){const t=x(e);return(0,g.jsxs)("div",{className:(0,l.A)(a.G.tabs.container,"tabs-container",y),children:[(0,g.jsx)(j,{...t,...e}),(0,g.jsx)(E,{...t,...e})]})}function C(e){const t=(0,f.A)();return(0,g.jsx)(T,{...e,children:h(e.children)},String(t))}},8453(e,t,r){r.d(t,{R:()=>o,x:()=>s});var n=r(6540);const l={},a=n.createContext(l);function o(e){const t=n.useContext(a);return n.useMemo(function(){return"function"==typeof e?e(t):{...t,...e}},[t,e])}function s(e){let t;return t=e.disableParentContext?"function"==typeof e.components?e.components(l):e.components||l:o(e.components),n.createElement(a.Provider,{value:t},e.children)}}}]);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.