func Publish(cid, key string) error {
	_, err := doRequest(0, fmt.Sprintf("name/publish?arg=%s&key=%s", url.QueryEscape(cid), KeySpace+key))  
	return err
}
Publish CID to IPNS
package main
import (
	"bytes"
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/fs"
	"io/ioutil"
	"log"
	"mime/multipart"
	"net/http"
	"net/textproto"
	"net/url"
	"os"
	"path/filepath"
	"strings"
	"time"
)
const (
	KeySpace = "ipfs-sync."
	API      = "/api/v0/"
)
func findInStringSlice(slice []string, val string) int {
	for i, item := range slice {
		if item == val {
			return i
		}
	}
	return -1
}
func doRequest(timeout time.Duration, cmd string) (string, error) {
	var cancel context.CancelFunc
	ctx := context.Background()
	if timeout > 0 {
		ctx, cancel = context.WithTimeout(ctx, timeout)
		defer cancel()
	}
	c := &http.Client{}
	req, err := http.NewRequestWithContext(ctx, "POST", EndPoint+API+cmd, nil)
	if err != nil {
		return "", err
	}
	resp, err := c.Do(req)
	if err != nil {
		return "", err
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return "", err
	}
	errStruct := new(ErrorStruct)
	err = json.Unmarshal(body, errStruct)
	if err == nil {
		if errStruct.Error() != "" {
			return string(body), errStruct
		}
	}
	return string(body), nil
}
type HashStruct struct {
	Hash string
}
func GetFileCID(filePath string) string {
	out, _ := doRequest(TimeoutTime, "files/stat?hash=true&arg="+url.QueryEscape(BasePath+filePath))
	fStat := new(HashStruct)
	err := json.Unmarshal([]byte(out), &fStat)
	if err != nil {
		return ""
	}
	return fStat.Hash
}
func RemoveFile(fpath string) error {
	_, err := doRequest(TimeoutTime, fmt.Sprintf(`files/rm?arg=%s&force=true`, url.QueryEscape(BasePath+fpath)))
	return err
}
func MakeDir(path string) error {
	_, err := doRequest(TimeoutTime, fmt.Sprintf(`files/mkdir?arg=%s&parents=true`, url.QueryEscape(BasePath+path)))
	return err
}
func filePathWalkDir(root string) ([]string, error) {
	var files []string
	err := filepath.WalkDir(root, func(path string, info fs.DirEntry, err error) error {
		if info == nil {
			return errors.New(fmt.Sprintf("cannot access '%s' for crawling", path))
		}
		if !info.IsDir() {
			filePathSplit := strings.Split(path, string(os.PathSeparator))
			if IgnoreHidden && filePathSplit[len(filePathSplit)-1][0] == '.' {
				return nil
			}
			files = append(files, path)
		} else {
			dirPathSplit := strings.Split(path, string(os.PathSeparator))
			if IgnoreHidden && len(dirPathSplit[len(dirPathSplit)-1]) > 0 && dirPathSplit[len(dirPathSplit)-1][0] == '.' {
				return filepath.SkipDir
			}
		}
		return nil
	})
	return files, err
}
func AddDir(path string, nocopy bool, pin bool, estuary bool) (string, error) {
	pathSplit := strings.Split(path, string(os.PathSeparator))
	dirName := pathSplit[len(pathSplit)-2]
	files, err := filePathWalkDir(path)
	if err != nil {
		return "", err
	}
	localDirs := make(map[string]bool)
	for _, file := range files {
		filePathSplit := strings.Split(file, string(os.PathSeparator))
		if IgnoreHidden && filePathSplit[len(filePathSplit)-1][0] == '.' {
			continue
		}
		splitName := strings.Split(file, ".")
		if findInStringSlice(Ignore, splitName[len(splitName)-1]) > -1 {
			continue
		}
		parentDir := strings.Join(filePathSplit[:len(filePathSplit)-1], string(os.PathSeparator))
		makeDir := !localDirs[parentDir]
		if makeDir {
			localDirs[parentDir] = true
		}
		mfsPath := file[len(path):]
		if os.PathSeparator != '/' {
			mfsPath = strings.ReplaceAll(mfsPath, string(os.PathSeparator), "/")
		}
		_, err := AddFile(file, dirName+"/"+mfsPath, nocopy, makeDir, false)
		if err != nil {
			log.Println("Error adding file:", err)
		}
	}
	cid := GetFileCID(dirName)
	if pin {
		err := Pin(cid)
		log.Println("Error pinning", dirName, ":", err)
	}
	if estuary {
		if err := PinEstuary(cid, dirName); err != nil {
			log.Println("Error pinning to Estuary:", err)
		}
	}
	return cid, err
}
func IPFSAddFile(fpath string, nocopy, onlyhash bool) (*HashStruct, error) {
	client := http.Client{}
	f, err := os.Open(fpath)
	if err != nil {
		return nil, err
	}
	pr, pw := io.Pipe()
	writer := multipart.NewWriter(pw)
	defer pr.Close()
	req, err := http.NewRequest("POST", EndPoint+API+fmt.Sprintf(`add?nocopy=%t&pin=false&quieter=true&only-hash=%t`, nocopy, onlyhash), pr)
	if err != nil {
		return nil, err
	}
	req.Header.Add("Content-Type", writer.FormDataContentType())
	go func() {
		defer f.Close()
		defer writer.Close()
		h := make(textproto.MIMEHeader)
		h.Set("Abspath", fpath)
		h.Set("Content-Disposition", fmt.Sprintf(`form-data; name="%s"; filename="%s"`, "file", url.QueryEscape(f.Name())))
		h.Set("Content-Type", "application/octet-stream")
		part, err := writer.CreatePart(h)
		if err != nil {
			pw.CloseWithError(err)
			return
		}
		if Verbose {
			log.Println("Generating file headers...")
		}
		_, err = io.Copy(part, f)
		pw.CloseWithError(err)
	}()
	if Verbose {
		log.Println("Doing add request...")
	}
	resp, err := client.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	var hash HashStruct
	err = json.NewDecoder(resp.Body).Decode(&hash)
	if Verbose {
		log.Println("File hash:", hash.Hash)
	}
	return &hash, err
}
func AddFile(from, to string, nocopy bool, makedir bool, overwrite bool) (string, error) {
	log.Println("Adding file from", from, "to", BasePath+to, "...")
	hash, err := IPFSAddFile(from, nocopy, false)
	if err != nil {
		return "", err
	}
	if makedir {
		toSplit := strings.Split(to, "/")
		parent := strings.Join(toSplit[:len(toSplit)-1], "/")
		if Verbose {
			log.Printf("Creating parent directory '%s' in MFS...\n", parent)
		}
		err = MakeDir(parent)
		if err != nil {
			return "", err
		}
	}
	if overwrite {
		if Verbose {
			log.Println("Removing existing file (if any)...")
		}
		RemoveFile(to)
	}
	if Verbose {
		log.Println("Adding file to mfs path:", BasePath+to)
	}
	_, err = doRequest(TimeoutTime, fmt.Sprintf(`files/cp?arg=%s&arg=%s`, "/ipfs/"+url.QueryEscape(hash.Hash), url.QueryEscape(BasePath+to)))
	if err != nil {
		if Verbose {
			log.Println("Error on files/cp:", err)
			log.Println("fpath:", from)
		}
		if HandleBadBlockError(err, from, nocopy) {
			log.Println("files/cp failure due to filestore, retrying (recursive)")
			AddFile(from, to, nocopy, makedir, overwrite)
		}
	}
	return hash.Hash, err
}
type FileStoreStatus int
const NoFile FileStoreStatus = 11
type FileStoreKey struct {
	Slash string `json:"/"`
}
type FileStoreEntry struct {
	Status FileStoreStatus
	Key    FileStoreKey
}
var fileStoreCleanupLock chan int
func init() {
	fileStoreCleanupLock = make(chan int, 1)
}
type RefResp struct {
	Err string
	Ref string
}
func RemoveCID(cid string) {
	var found bool
	c := &http.Client{}
	req, err := http.NewRequest("POST", EndPoint+API+"refs?unique=true&recursive=true&arg="+cid, nil)
	if err != nil {
		log.Println(err)
		return
	}
	resp, err := c.Do(req)
	if err != nil {
		log.Println(err)
		return
	}
	defer resp.Body.Close()
	dec := json.NewDecoder(resp.Body)
	if err != nil {
		log.Println(err)
		return
	}
	for dec.More() {
		found = true
		refResp := new(RefResp)
		err := dec.Decode(refResp)
		if err != nil {
			log.Println("Error decoding ref response stream:", err)
			continue
		}
		newcid := refResp.Ref
		if newcid == "" {
			newcid = cid
		}
		if Verbose {
			log.Println("Removing block:", newcid)
		}
		RemoveBlock(newcid)
	}
	if !found {
		if Verbose {
			log.Println("Removing block:", cid)
		}
		RemoveBlock(cid)
	}
}
func RemoveBlock(cid string) {
	var err error
	for _, err = doRequest(TimeoutTime, "block/rm?arg="+cid); err != nil && strings.HasPrefix(err.Error(), "pinned"); _, err = doRequest(TimeoutTime, "block/rm?arg="+cid) {
		splitErr := strings.Split(err.Error(), " ")
		var cid2 string
		if len(splitErr) < 3 {  
			cid2 = cid
		} else {
			cid2 = splitErr[2]
		}
		log.Println("Effected block is pinned, removing pin:", cid2)
		_, err := doRequest(0, "pin/rm?arg="+cid2)  
		if err != nil {
			log.Println("Error removing pin:", err)
		}
	}
	if err != nil {
		log.Println("Error removing bad block:", err)
	}
}
func CleanFilestore() {
	select {
	case fileStoreCleanupLock <- 1:
		defer func() { <-fileStoreCleanupLock }()
	default:
		return
	}
	if Verbose {
		log.Println("Removing blocks that point to a file that doesn't exist from filestore...")
	}
	c := &http.Client{}
	req, err := http.NewRequest("POST", EndPoint+API+"filestore/verify", nil)
	if err != nil {
		log.Println(err)
		return
	}
	resp, err := c.Do(req)
	if err != nil {
		log.Println(err)
		return
	}
	defer resp.Body.Close()
	dec := json.NewDecoder(resp.Body)
	if err != nil {
		log.Println(err)
		return
	}
	for dec.More() {
		fsEntry := new(FileStoreEntry)
		err := dec.Decode(fsEntry)
		if err != nil {
			log.Println("Error decoding fsEntry stream:", err)
			continue
		}
		if fsEntry.Status == NoFile {  
			log.Println("Removing reference from filestore:", fsEntry.Key.Slash)
			RemoveBlock(fsEntry.Key.Slash)
		}
	}
}
func HandleBadBlockError(err error, fpath string, nocopy bool) bool {
	txt := err.Error()
	if strings.HasPrefix(txt, "failed to get block") || strings.HasSuffix(txt, "no such file or directory") {
		if Verbose {
			log.Println("Handling bad block error: " + txt)
		}
		if fpath == "" {  
			CleanFilestore()
		} else {
			cid, err := IPFSAddFile(fpath, nocopy, true)
			if err == nil {
				RemoveCID(cid.Hash)
			} else {
				log.Println("Error handling bad block error:", err)
			}
		}
		return true
	}
	return false
}
func Pin(cid string) error {
	resp, err := doRequest(0, "pin/add?arg="+url.QueryEscape(cid))  
	if resp != "" {
		if Verbose {
			log.Println("Pin response:", resp)
		}
	}
	return err
}
type ErrorStruct struct {
	Message string  
	Error2  string `json:"Error"`  
	Code    int
	Type    string
}
func (es *ErrorStruct) Error() string {
	switch {
	case es.Message != "":
		return es.Message
	case es.Error2 != "":
		return es.Error2
	}
	return ""
}
func UpdatePin(from, to string, nocopy bool) {
	_, err := doRequest(0, "pin/update?arg="+url.QueryEscape(from)+"&arg="+url.QueryEscape(to))  
	if err != nil {
		log.Println("Error updating pin:", err)
		if Verbose {
			log.Println("From CID:", from, "To CID:", to)
		}
		if HandleBadBlockError(err, "", nocopy) {
			if Verbose {
				log.Println("Bad blocks found, running pin/update again (recursive)")
			}
			UpdatePin(from, to, nocopy)
			return
		}
		err = Pin(to)
		if err != nil {
			log.Println("[ERROR] Error adding pin:", err)
		}
	}
}
type Key struct {
	Id   string
	Name string
}
type Keys struct {
	Keys []Key
}
func ListKeys() (*Keys, error) {
	res, err := doRequest(TimeoutTime, "key/list")
	if err != nil {
		return nil, err
	}
	keys := new(Keys)
	err = json.Unmarshal([]byte(res), keys)
	if err != nil {
		return nil, err
	}
	return keys, nil
}
func ResolveIPNS(key string) (string, error) {
	res, err := doRequest(0, "name/resolve?arg="+key)  
	if err != nil {
		return "", err
	}
	type PathStruct struct {
		Path string
	}
	path := new(PathStruct)
	err = json.Unmarshal([]byte(res), path)
	if err != nil {
		return "", err
	}
	pathSplit := strings.Split(path.Path, "/")
	if len(pathSplit) < 3 {
		return "", errors.New("Unexpected output in name/resolve: " + path.Path)
	}
	return pathSplit[2], nil
}
func GenerateKey(name string) Key {
	res, err := doRequest(TimeoutTime, "key/gen?arg="+KeySpace+name)
	if err != nil {
		log.Panicln("[ERROR]", err)
	}
	key := new(Key)
	err = json.Unmarshal([]byte(res), key)
	if err != nil {
		log.Panicln("[ERROR]", err)
	}
	return *key
}