diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
new file mode 100644
index 0000000..4d84c6f
--- /dev/null
+++ b/.github/workflows/build.yml
@@ -0,0 +1,71 @@
+name: Build hjson-cli Releases
+
+on:
+  push:
+    tags:
+      - 'v[0-9]+.[0-9]+**'
+
+jobs:
+  
+  build_hjson-cli_releases:
+    runs-on: ${{ matrix.os }}
+    
+    strategy:
+      fail-fast: false
+      matrix:
+        os: [ubuntu-latest, macos-latest, windows-latest]
+        
+    steps:
+      - name: Checkout repo
+        uses: actions/checkout@v2
+          
+      - name: Build the ${{ runner.os }} hjson-cli binary
+        run: |
+          mkdir binaries
+          cd hjson-cli
+          go build
+          
+      - name: Rename hjson-cli binary to reflect ${{ runner.os }}
+        run: |
+          mv hjson-cli/hjson-cli.exe binaries/hjson-cli_${{ github.ref_name }}_${{ runner.os }}.exe
+        if: ${{ contains(matrix.os, 'windows') }}
+        
+      - name: Rename hjson-cli binary to reflect ${{ runner.os }}
+        run: |
+          mv hjson-cli/hjson-cli binaries/hjson-cli_${{ github.ref_name }}_${{ runner.os }}
+        if: runner.os != 'Windows'
+        
+      - name: Upload hjson-cli artifacts
+        uses: actions/upload-artifact@v2
+        with:
+          name: output
+          path: binaries/*
+          if-no-files-found: error
+  
+  
+  release_artifacts:
+    needs: [build_hjson-cli_releases]
+    runs-on: ubuntu-latest
+    steps:
+    - name: Download actifacts
+      uses: actions/download-artifact@v2
+      with:
+        path: artifacts
+        
+    - name: Show the downloaded artifacts
+      run: |
+        pwd
+        ls -laR *
+        
+    - name: Release binaries
+      uses: ncipollo/release-action@v1
+      with:
+        # ncipollo/release-action needs a tag! Either a usual "GIT TAG" or an dedicated TAG, see below!
+        # set a TAG if you want to build a release, i.e. via "workflow_dispatch" on GitHub _AND_ do not push a regular GIT TAG
+        # and the other way around, if you want to build releases based on pushed GIT TAGs, make sure you un-comment the "tag:" line below!
+        tag: ${{ github.ref_name }} 
+        draft: true
+        artifactErrorsFailBuild: true
+        artifacts: "artifacts/output/*"
+        token: ${{ secrets.GITHUB_TOKEN }}
+        
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
new file mode 100644
index 0000000..b92e8f1
--- /dev/null
+++ b/.github/workflows/test.yml
@@ -0,0 +1,16 @@
+name: test
+on: [push, pull_request]
+jobs:
+  test:
+    runs-on: ${{ matrix.os }}
+    strategy:
+      fail-fast: false
+      matrix:
+        os: [ubuntu-latest, macos-latest, windows-latest]
+    steps:
+      - uses: actions/checkout@v2
+      - run: go version
+      - run: go test -v
+      - run: |
+          cd hjson-cli
+          go install -i
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index e7084d1..0000000
--- a/.travis.yml
+++ /dev/null
@@ -1,48 +0,0 @@
-language: go
-go:
-- 1.6
-- 1.7
-- 1.8
-- tip
-install: true
-script:
-- go install github.com/hjson/hjson-go
-- go install github.com/hjson/hjson-go/hjson-cli
-- go test -v github.com/hjson/hjson-go
-before_deploy:
-  - ./build_release.sh
-deploy:
-  provider: releases
-  file:
-  - _dist/darwin_386.tar.gz
-  - _dist/darwin_amd64.tar.gz
-  - _dist/dragonfly_amd64.tar.gz
-  - _dist/freebsd_386.tar.gz
-  - _dist/freebsd_amd64.tar.gz
-  - _dist/freebsd_arm.tar.gz
-  - _dist/linux_386.tar.gz
-  - _dist/linux_amd64.tar.gz
-  - _dist/linux_arm.tar.gz
-  - _dist/linux_arm64.tar.gz
-  - _dist/linux_mips64.tar.gz
-  - _dist/linux_mips64le.tar.gz
-  - _dist/linux_ppc64.tar.gz
-  - _dist/linux_ppc64le.tar.gz
-  - _dist/netbsd_386.tar.gz
-  - _dist/netbsd_amd64.tar.gz
-  - _dist/netbsd_arm.tar.gz
-  - _dist/openbsd_386.tar.gz
-  - _dist/openbsd_amd64.tar.gz
-  - _dist/openbsd_arm.tar.gz
-  - _dist/plan9_386.tar.gz
-  - _dist/plan9_amd64.tar.gz
-  - _dist/solaris_amd64.tar.gz
-  - _dist/windows_386.zip
-  - _dist/windows_amd64.zip
-  overwrite: true
-  skip_cleanup: true
-  on:
-    tags: true
-    go: 1.7
-  api-key:
-    secure: RAnrBG7xantKdUBlIsv8Od9yZ0GTwCsJ7d1V+hSEC3JSz76+Ie4v1nJw+rD0Rp25zxDAM2qSkMfIaQJVSQ5tYg3N8C/d+/lfeRNBwLhHEsQztsaE5BU0I/qrkKq0pGMoG+cu6ZJ06xEgECPKOamwQ2wSY0IiIwVn0eNN/ufMsZUTmPgBE4+lx9eEWYDqMxy4tfgLMC/ry2fnA3zc4fQk0TboodLkI0SgwMq7cHcm95j9kHWpydbwr8IhQ6pucLM9YuKGv2fNHywlQ6Z9rhpYdiK/9KUofW20Rm4OB1MBHLLhLdQ0qCkVPswhGRdFPcBcN7lvv8Czcp3Hl4LQ4UzxOXf58mEWbh4r6VbUjZgRnpMy8Ms9CcyLjwaRbJPKA/kI53yjeEViFbDlOtBV8qdVqx5orCUrpU2ML/iEltVDL7pukhjuKd0K/2c3s8gJwH4LaGEfQjj2DvYkQgK0XsWSOXhYF4LaN0qhiQ3xiss1bhjJIIIhD3PHPy+dCsazKoJtNRHoUyP9RYz8EjcwI7BsaEEzskaXuUSvLz3+k6f6DZvPydUDXDGYqX0H2dXCT5H0IaCrSAkDVcGageXt4vkzKb45/el1W5VME/q/WpGAIPfsJ2beR7ALlm1RhXdWTJpwq6LM/YbYy/6U/HlqQCwB4x/nwKXy1ToDukCrHy57yGU=
diff --git a/README.md b/README.md
index e6a2d64..b13046a 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
 # hjson-go
 
-[![Build Status](https://img.shields.io/travis/hjson/hjson-go.svg?style=flat-square)](https://travis-ci.org/hjson/hjson-go)
+[![Build Status](https://github.com/hjson/hjson-go/workflows/test/badge.svg)](https://github.com/hjson/hjson-go/actions)
 [![Go Pkg](https://img.shields.io/github/release/hjson/hjson-go.svg?style=flat-square&label=go-pkg)](https://github.com/hjson/hjson-go/releases)
 [![Go Report Card](https://goreportcard.com/badge/github.com/hjson/hjson-go?style=flat-square)](https://goreportcard.com/report/github.com/hjson/hjson-go)
 [![coverage](https://img.shields.io/badge/coverage-ok-brightgreen.svg?style=flat-square)](http://gocover.io/github.com/hjson/hjson-go/)
diff --git a/assets/sorted/strings_result.hjson b/assets/sorted/strings_result.hjson
index 038b1c5..b94daa7 100644
--- a/assets/sorted/strings_result.hjson
+++ b/assets/sorted/strings_result.hjson
@@ -34,6 +34,7 @@
     last line
 
     '''
+  multiline4: ←→±≠Я
   not:
   {
     array:
@@ -69,6 +70,7 @@
     one: "1"
     true: "true"
     two: "2"
+    zero: "0"
   }
   text1: This is a valid string value.
   text2: a \ is just a \
diff --git a/assets/sorted/strings_result.json b/assets/sorted/strings_result.json
index 1c93c88..b00ad43 100644
--- a/assets/sorted/strings_result.json
+++ b/assets/sorted/strings_result.json
@@ -17,6 +17,7 @@
   "multiline1": "first line\n  indented line\nlast line",
   "multiline2": "first line\n  indented line\nlast line",
   "multiline3": "first line\n  indented line\nlast line\n",
+  "multiline4": "←→±≠Я",
   "not": {
     "array": [
       1,
@@ -48,7 +49,8 @@
     "null": "null",
     "one": "1",
     "true": "true",
-    "two": "2"
+    "two": "2",
+    "zero": "0"
   },
   "text1": "This is a valid string value.",
   "text2": "a \\ is just a \\",
diff --git a/assets/strings_result.hjson b/assets/strings_result.hjson
index 6569fe7..95e04a1 100644
--- a/assets/strings_result.hjson
+++ b/assets/strings_result.hjson
@@ -28,6 +28,7 @@
     last line
 
     '''
+  multiline4: ←→±≠Я
   foo1a: asdf\"'a\s\w
   foo1b: asdf\"'a\s\w
   foo1c: asdf\"'a\s\w
@@ -75,5 +76,6 @@
     one: "1"
     two: "2"
     minus: "-3"
+    zero: "0"
   }
 }
\ No newline at end of file
diff --git a/assets/strings_result.json b/assets/strings_result.json
index a2dd2ef..bc4c610 100644
--- a/assets/strings_result.json
+++ b/assets/strings_result.json
@@ -12,6 +12,8 @@
   "multiline1": "first line\n  indented line\nlast line",
   "multiline2": "first line\n  indented line\nlast line",
   "multiline3": "first line\n  indented line\nlast line\n",
+  "multiline4": "←→±≠Я",
+
   "foo1a": "asdf\\\"'a\\s\\w",
   "foo1b": "asdf\\\"'a\\s\\w",
   "foo1c": "asdf\\\"'a\\s\\w",
@@ -54,6 +56,7 @@
     "null": "null",
     "one": "1",
     "two": "2",
-    "minus": "-3"
+    "minus": "-3",
+    "zero": "0"
   }
 }
\ No newline at end of file
diff --git a/assets/strings_test.hjson b/assets/strings_test.hjson
index 4b77f70..8efee41 100644
--- a/assets/strings_test.hjson
+++ b/assets/strings_test.hjson
@@ -37,6 +37,11 @@
 
     ''' # trailing lf
 
+  multiline4:
+    '''
+    ←→±≠Я
+    '''
+
   # escapes/no escape
 
   foo1a: asdf\"'a\s\w
@@ -81,5 +86,6 @@
     one: "1"
     two: "2"
     minus: "-3"
+    zero: "0"
   }
 }
diff --git a/debian/changelog b/debian/changelog
index 46edb5f..813afd8 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+hjson-go (3.2.0-1) UNRELEASED; urgency=low
+
+  * New upstream release.
+
+ -- Debian Janitor <janitor@jelmer.uk>  Sat, 28 May 2022 18:58:58 -0000
+
 hjson-go (3.1.0-2) unstable; urgency=medium
 
   * Source-only upload to facilitate migration to testing
diff --git a/encode.go b/encode.go
index ae7acd6..478eb56 100644
--- a/encode.go
+++ b/encode.go
@@ -20,12 +20,16 @@ type EncoderOptions struct {
 	Eol string
 	// Place braces on the same line
 	BracesSameLine bool
-	// Deprecated: Hjson always emits braces
+	// Emit braces for the root object
 	EmitRootBraces bool
 	// Always place string in quotes
 	QuoteAlways bool
+	// Place string in quotes if it could otherwise be a number, boolean or null
+	QuoteAmbiguousStrings bool
 	// Indent string
 	IndentBy string
+	// Base indentation string
+	BaseIndentation string
 	// Allow the -0 value (unlike ES6)
 	AllowMinusZero bool
 	// Encode unknown values as 'null'
@@ -39,7 +43,9 @@ func DefaultOptions() EncoderOptions {
 	opt.BracesSameLine = false
 	opt.EmitRootBraces = true
 	opt.QuoteAlways = false
+	opt.QuoteAmbiguousStrings = true
 	opt.IndentBy = "  "
+	opt.BaseIndentation = ""
 	opt.AllowMinusZero = false
 	opt.UnknownAsNull = false
 	return opt
@@ -97,9 +103,8 @@ func (e *hjsonEncoder) quote(value string, separator string, isRootObject bool)
 	if len(value) == 0 {
 		e.WriteString(separator + `""`)
 	} else if e.QuoteAlways ||
-		needsQuotes.MatchString(value) ||
-		startsWithNumber([]byte(value)) ||
-		startsWithKeyword.MatchString(value) {
+		needsQuotes.MatchString(value) || (e.QuoteAmbiguousStrings && (startsWithNumber([]byte(value)) ||
+		startsWithKeyword.MatchString(value))) {
 
 		// If the string contains no control characters, no quote characters, and no
 		// backslash characters, then we can safely slap some quotes around it.
@@ -178,6 +183,7 @@ func (s sortAlpha) Less(i, j int) bool {
 
 func (e *hjsonEncoder) writeIndent(indent int) {
 	e.WriteString(e.Eol)
+	e.WriteString(e.BaseIndentation)
 	for i := 0; i < indent; i++ {
 		e.WriteString(e.IndentBy)
 	}
@@ -207,8 +213,7 @@ func (e *hjsonEncoder) str(value reflect.Value, noIndent bool, separator string,
 			e.WriteString("null")
 			return nil
 		}
-		value = value.Elem()
-		kind = value.Kind()
+		return e.str(value.Elem(),noIndent,separator,isRootObject)
 	}
 
 	if value.Type().Implements(marshaler) {
@@ -296,20 +301,25 @@ func (e *hjsonEncoder) str(value reflect.Value, noIndent bool, separator string,
 		}
 
 		indent1 := e.indent
-		e.indent++
-		if !noIndent && !e.BracesSameLine {
-			e.writeIndent(indent1)
-		} else {
-			e.WriteString(separator)
+		if !isRootObject || e.EmitRootBraces {
+			if !noIndent && !e.BracesSameLine {
+				e.writeIndent(e.indent)
+			} else {
+				e.WriteString(separator)
+			}
+
+			e.indent++
+			e.WriteString("{")
 		}
-		e.WriteString("{")
 
 		keys := value.MapKeys()
 		sort.Sort(sortAlpha(keys))
 
 		// Join all of the member texts together, separated with newlines
 		for i := 0; i < len; i++ {
-			e.writeIndent(e.indent)
+			if i > 0 || !isRootObject || e.EmitRootBraces {
+				e.writeIndent(e.indent)
+			}
 			e.WriteString(e.quoteName(keys[i].String()))
 			e.WriteString(":")
 			if err := e.str(value.MapIndex(keys[i]), false, " ", false); err != nil {
@@ -317,8 +327,10 @@ func (e *hjsonEncoder) str(value reflect.Value, noIndent bool, separator string,
 			}
 		}
 
-		e.writeIndent(indent1)
-		e.WriteString("}")
+		if !isRootObject || e.EmitRootBraces {
+			e.writeIndent(indent1)
+			e.WriteString("}")
+		}
 		e.indent = indent1
 
 	case reflect.Struct:
@@ -331,13 +343,16 @@ func (e *hjsonEncoder) str(value reflect.Value, noIndent bool, separator string,
 		}
 
 		indent1 := e.indent
-		e.indent++
-		if !noIndent && !e.BracesSameLine {
-			e.writeIndent(indent1)
-		} else {
-			e.WriteString(separator)
+		if !isRootObject || e.EmitRootBraces {
+			if !noIndent && !e.BracesSameLine {
+				e.writeIndent(e.indent)
+			} else {
+				e.WriteString(separator)
+			}
+
+			e.indent++
+			e.WriteString("{")
 		}
-		e.WriteString("{")
 
 		// Join all of the member texts together, separated with newlines
 		for i := 0; i < l; i++ {
@@ -367,11 +382,15 @@ func (e *hjsonEncoder) str(value reflect.Value, noIndent bool, separator string,
 			}
 			if len(jsonComment) > 0 {
 				for _, line := range strings.Split(jsonComment, e.Eol) {
-					e.writeIndent(e.indent)
+					if i > 0 || !isRootObject || e.EmitRootBraces {
+						e.writeIndent(e.indent)
+					}
 					e.WriteString(fmt.Sprintf("# %s", line))
 				}
 			}
-			e.writeIndent(e.indent)
+			if i > 0 || !isRootObject || e.EmitRootBraces {
+				e.writeIndent(e.indent)
+			}
 			e.WriteString(e.quoteName(name))
 			e.WriteString(":")
 			if err := e.str(curField, false, " ", false); err != nil {
@@ -382,8 +401,10 @@ func (e *hjsonEncoder) str(value reflect.Value, noIndent bool, separator string,
 			}
 		}
 
-		e.writeIndent(indent1)
-		e.WriteString("}")
+		if !isRootObject || e.EmitRootBraces {
+			e.writeIndent(indent1)
+			e.WriteString("}")
+		}
 
 		e.indent = indent1
 
@@ -457,10 +478,13 @@ func MarshalWithOptions(v interface{}, options EncoderOptions) ([]byte, error) {
 	e.indent = 0
 	e.Eol = options.Eol
 	e.BracesSameLine = options.BracesSameLine
+	e.EmitRootBraces = options.EmitRootBraces
 	e.QuoteAlways = options.QuoteAlways
+	e.QuoteAmbiguousStrings = options.QuoteAmbiguousStrings
 	e.IndentBy = options.IndentBy
+	e.BaseIndentation = options.BaseIndentation
 
-	err := e.str(reflect.ValueOf(v), true, "", true)
+	err := e.str(reflect.ValueOf(v), true, e.BaseIndentation, true)
 	if err != nil {
 		return nil, err
 	}
diff --git a/encode_test.go b/encode_test.go
index 0b97e6c..6011777 100644
--- a/encode_test.go
+++ b/encode_test.go
@@ -149,3 +149,90 @@ func TestEncodeMarshal(t *testing.T) {
 		t.Error("Marshaler interface error")
 	}
 }
+
+func TestEncodeSliceOfPtrOfPtrOfString(t *testing.T) {
+	s:="1"
+	s1:=&s
+	input:=[]**string{&s1}
+	buf, err := Marshal(input)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+	if !reflect.DeepEqual(buf, []byte(`[
+  "1"
+]`)) {
+		t.Error("Marshaler interface error")
+	}
+}
+
+func TestNoRootBraces(t *testing.T) {
+	input := struct {
+		Foo string
+	}{
+		Foo: "Bar",
+	}
+	opt := DefaultOptions()
+	opt.EmitRootBraces = false
+	buf, err := MarshalWithOptions(input, opt)
+	if err != nil {
+		t.Error(err)
+	}
+	if !reflect.DeepEqual(buf, []byte(`Foo: Bar`)) {
+		t.Error("Encode struct with EmitRootBraces false")
+	}
+
+	theMap := map[string]interface{}{
+		"Foo": "Bar",
+	}
+	buf, err = MarshalWithOptions(theMap, opt)
+	if err != nil {
+		t.Error(err)
+	}
+	if !reflect.DeepEqual(buf, []byte(`Foo: Bar`)) {
+		t.Error("Encode map with EmitRootBraces false")
+	}
+}
+
+func TestBaseIndentation(t *testing.T) {
+	input := struct {
+		Foo string
+	}{
+		Foo: "Bar",
+	}
+	facit := []byte(`   {
+     Foo: Bar
+   }`)
+	opt := DefaultOptions()
+	opt.BaseIndentation = "   "
+	buf, err := MarshalWithOptions(input, opt)
+	if err != nil {
+		t.Error(err)
+	}
+	if !reflect.DeepEqual(buf, facit) {
+		t.Error("Encode with BaseIndentation, comparison:\n", string(buf), "\n", string(facit))
+	}
+}
+
+func TestQuoteAmbiguousStrings(t *testing.T) {
+	theMap := map[string]interface{}{
+		"One":   "1",
+		"Null":  "null",
+		"False": "false",
+	}
+	facit := []byte(`{
+  False: false
+  Null: null
+  One: 1
+}`)
+	opt := DefaultOptions()
+	opt.QuoteAlways = false
+	opt.QuoteAmbiguousStrings = false
+	buf, err := MarshalWithOptions(theMap, opt)
+	if err != nil {
+		t.Error(err)
+	}
+	if !reflect.DeepEqual(buf, facit) {
+		t.Error("Encode with QuoteAmbiguousStrings false, comparison:\n", string(buf), "\n", string(facit))
+	}
+}
diff --git a/go.mod b/go.mod
new file mode 100644
index 0000000..4310e9b
--- /dev/null
+++ b/go.mod
@@ -0,0 +1,3 @@
+module github.com/hjson/hjson-go
+
+go 1.6
diff --git a/hjson_test.go b/hjson_test.go
index 2645747..ce61ecd 100644
--- a/hjson_test.go
+++ b/hjson_test.go
@@ -16,7 +16,9 @@ func getContent(file string) []byte {
 	if data, err := ioutil.ReadFile(file); err != nil {
 		panic(err)
 	} else {
-		return data
+		// The output from Marshal() always uses Unix EOL, but git might have
+		// converted files to Windows EOL on Windows, therefore we remove all "\r".
+		return bytes.Replace(data, []byte("\r"), []byte(""), -1)
 	}
 }