diff --git a/openrpc/cmd/cli.go b/openrpc/cmd/cli.go
index f9962a887be718cdcb9fb52cf6eba9bc412bdd34..a6d0bd067d21918f0d0788642f400c8cd9d02823 100644
--- a/openrpc/cmd/cli.go
+++ b/openrpc/cmd/cli.go
@@ -1,12 +1,12 @@
 package main
 
-import "C"
 import (
 	"encoding/json"
 	"fmt"
 	"os"
 
 	"gfx.cafe/open/jrpc/openrpc/generate"
+	"gfx.cafe/open/jrpc/openrpc/templates"
 
 	"gfx.cafe/open/jrpc/openrpc/types"
 	"github.com/alecthomas/kong"
@@ -15,6 +15,15 @@ import (
 var CLI struct {
 	Compile  CompileCommand  `cmd:"" help:"Compile a folder into a single openrpc spec"`
 	Generate GenerateCommand `cmd:"" help:"Compile a folder into a single openrpc spec"`
+	Template TemplateCommand `cmd:"" help:"print template to stdout"`
+}
+
+type TemplateCommand struct {
+}
+
+func (c *TemplateCommand) Run() error {
+	fmt.Print(templates.TEMPLATE)
+	return nil
 }
 
 type CompileCommand struct {
diff --git a/openrpc/generate/generate.go b/openrpc/generate/generate.go
index 6bb5a81a97602e368570da1e69c5325bc8cd8d80..52017e89cd62b6f8c61d55a45534ed10ddc61293 100644
--- a/openrpc/generate/generate.go
+++ b/openrpc/generate/generate.go
@@ -44,7 +44,6 @@ var funcs = template.FuncMap{
 }
 
 func Generate(rpc *types.OpenRPC, ts string, output string) error {
-
 	var wr bytes.Buffer
 	t, err := template.New(path.Base(ts)).Funcs(funcs).ParseFiles(ts)
 	if err != nil {
diff --git a/openrpc/templates/generate.sh b/openrpc/templates/generate.sh
new file mode 100755
index 0000000000000000000000000000000000000000..0eaf74dcab79b9aded34106999d425f24e2bfe62
--- /dev/null
+++ b/openrpc/templates/generate.sh
@@ -0,0 +1,2 @@
+#!/bin/sh
+cat types.gotmpl | sed 's/`/`\+"`"\+`/g' | sed '1s/^/package templates \nconst TEMPLATE=`/' | sed '$a`' > template.go
diff --git a/openrpc/templates/provide.go b/openrpc/templates/provide.go
new file mode 100644
index 0000000000000000000000000000000000000000..39409ff5e10181b6553f16c650926a35c1c5ac16
--- /dev/null
+++ b/openrpc/templates/provide.go
@@ -0,0 +1,3 @@
+package templates
+
+//go:generate sh generate.sh
diff --git a/openrpc/templates/template.go b/openrpc/templates/template.go
new file mode 100644
index 0000000000000000000000000000000000000000..7b49130e8e8065e4d5f757eb4a569c3f4ad6f427
--- /dev/null
+++ b/openrpc/templates/template.go
@@ -0,0 +1,53 @@
+package templates 
+const TEMPLATE=`// Code generated by go-openrpc. DO NOT EDIT.
+
+package {{ .Package }}
+
+{{define "schemaType" -}}
+    {{if not (eq .Ref "") -}}
+        {{camelCase (refName .Ref)}}
+    {{- else if or
+        (not (eq (len .Properties) 0))
+        (not (eq (len .OneOf) 0))
+        (not (eq (len .AnyOf) 0))
+        (not (eq (len .AllOf) 0))
+    -}}
+        struct {
+            {{range $name, $property := .Properties -}}
+                {{camelCase $name}} {{template "schemaType" $property}} `+"`"+`json:"{{$name}}"`+"`"+`
+            {{end -}}
+            {{range $idx, $property := .OneOf -}}
+                Option{{$idx}} {{template "schemaType" $property}}
+            {{end -}}
+            {{range $idx, $property := .AnyOf -}}
+                Option{{$idx}} {{template "schemaType" $property}}
+            {{end -}}
+            {{range $idx, $property := .AllOf -}}
+                Field{{$idx}} {{template "schemaType" $property}}
+            {{end -}}
+        }
+    {{- else if eq .Type "array" -}}
+        []{{template "schemaType" (index .Items 0)}}
+    {{- else if not (eq (len .Enum) 0) -}}
+        string
+    {{- else -}}
+        {{goType .Type}}
+    {{- end}}
+{{- end -}}
+
+type GoOpenRPCService interface {
+    {{range .Methods -}}
+        // {{.Summary}}
+        {{camelCase .Name}}(
+            {{range .Params -}}
+                {{camelCase .Name}} {{template "schemaType" .Schema}},
+            {{end -}}
+        ) ({{camelCase .Result.Name}} {{template "schemaType" .Result.Schema}})
+    {{end -}}
+}
+
+{{- range $name, $component := .Components.Schemas }}
+    type {{camelCase $name}} {{template "schemaType" $component}}
+{{- end }}
+
+`