diff --git a/NEWS b/NEWS
index 511a88f..170021c 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,10 @@
+v0.11 (2021-08-17)
+  * Add support for contacting logs via HTTP proxies;
+    just set the appropriate environment variable as documented at
+    https://golang.org/pkg/net/http/#ProxyFromEnvironment
+  * Work around RFC 6962 ambiguity related to consistency proofs
+    for empty trees.
+
 v0.10 (2020-04-29)
   * Improve speed by processing logs in parallel
   * Add -start_at_end option to begin monitoring new logs at the end,
diff --git a/cmd/certspotter/main.go b/cmd/certspotter/main.go
index 00e62ca..c564ff7 100644
--- a/cmd/certspotter/main.go
+++ b/cmd/certspotter/main.go
@@ -221,8 +221,8 @@ func main() {
 			fmt.Fprintf(os.Stderr, "%s: %s: %s\n", os.Args[0], *watchlistFilename, err)
 			os.Exit(1)
 		}
-		defer file.Close()
 		watchlist, err = readWatchlist(file)
+		file.Close()
 		if err != nil {
 			fmt.Fprintf(os.Stderr, "%s: %s: %s\n", os.Args[0], *watchlistFilename, err)
 			os.Exit(1)
diff --git a/cmd/common.go b/cmd/common.go
index 54626a7..1bc44f0 100644
--- a/cmd/common.go
+++ b/cmd/common.go
@@ -269,7 +269,7 @@ func processLog(logInfo *loglist.Log, processCallback certspotter.ProcessCallbac
 	} else if *startAtEnd {
 		ctlog.tree, err = ctlog.scanner.MakeCollapsedMerkleTree(ctlog.verifiedSTH)
 		if err != nil {
-			log.Print("%s: Error reconstructing Merkle Tree: %s", logInfo.URL, err)
+			log.Printf("%s: Error reconstructing Merkle Tree: %s", logInfo.URL, err)
 			return 1
 		}
 		if *verbose {
diff --git a/cmd/state.go b/cmd/state.go
index 4b33243..5ecd117 100644
--- a/cmd/state.go
+++ b/cmd/state.go
@@ -162,7 +162,7 @@ func (state *State) SaveCert(isPrecert bool, certs [][]byte) (bool, string, erro
 }
 
 func (state *State) OpenLogState(logInfo *loglist.Log) (*LogState, error) {
-	return OpenLogState(filepath.Join(state.path, "logs", base64.RawURLEncoding.EncodeToString(logInfo.LogID)))
+	return OpenLogState(filepath.Join(state.path, "logs", base64.RawURLEncoding.EncodeToString(logInfo.LogID[:])))
 }
 
 func (state *State) GetLegacySTH(logInfo *loglist.Log) (*ct.SignedTreeHead, error) {
diff --git a/ct/client/logclient.go b/ct/client/logclient.go
index 209d680..e5adc09 100644
--- a/ct/client/logclient.go
+++ b/ct/client/logclient.go
@@ -90,6 +90,7 @@ func New(uri string) *LogClient {
 	var c LogClient
 	c.uri = uri
 	transport := &httpclient.Transport{
+		Proxy:                 http.ProxyFromEnvironment,
 		ConnectTimeout:        10 * time.Second,
 		RequestTimeout:        60 * time.Second,
 		ResponseHeaderTimeout: 30 * time.Second,
diff --git a/debian/changelog b/debian/changelog
index ee0a22c..fae3b61 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+certspotter (0.11-1) UNRELEASED; urgency=low
+
+  * New upstream release.
+
+ -- Debian Janitor <janitor@jelmer.uk>  Thu, 14 Apr 2022 22:44:05 -0000
+
 certspotter (0.10-1) unstable; urgency=medium
 
   * New upstream release.
diff --git a/loglist/helpers.go b/loglist/helpers.go
index 8f1a0f6..d236322 100644
--- a/loglist/helpers.go
+++ b/loglist/helpers.go
@@ -10,7 +10,6 @@
 package loglist
 
 import (
-	"encoding/base64"
 	"time"
 )
 
@@ -25,7 +24,7 @@ func (list *List) AllLogs() []*Log {
 }
 
 func (log *Log) LogIDString() string {
-	return base64.StdEncoding.EncodeToString(log.LogID)
+	return log.LogID.Base64String()
 }
 
 func (log *Log) AcceptsExpiration(expiration time.Time) bool {
diff --git a/loglist/load.go b/loglist/load.go
index 75b59dc..8fd5c4d 100644
--- a/loglist/load.go
+++ b/loglist/load.go
@@ -38,7 +38,7 @@ func Fetch(url string) (*List, error) {
 	if response.StatusCode != 200 {
 		return nil, fmt.Errorf("%s: %s", url, response.Status)
 	}
-	return unmarshal(content)
+	return Unmarshal(content)
 }
 
 func ReadFile(filename string) (*List, error) {
@@ -46,13 +46,16 @@ func ReadFile(filename string) (*List, error) {
 	if err != nil {
 		return nil, err
 	}
-	return unmarshal(content)
+	return Unmarshal(content)
 }
 
-func unmarshal(jsonBytes []byte) (*List, error) {
+func Unmarshal(jsonBytes []byte) (*List, error) {
 	list := new(List)
 	if err := json.Unmarshal(jsonBytes, list); err != nil {
 		return nil, err
 	}
+	if err := list.Validate(); err != nil {
+		return nil, fmt.Errorf("Invalid log list: %s", err)
+	}
 	return list, nil
 }
diff --git a/loglist/schema.go b/loglist/schema.go
index aa128ab..a5aa317 100644
--- a/loglist/schema.go
+++ b/loglist/schema.go
@@ -11,6 +11,8 @@ package loglist
 
 import (
 	"time"
+
+	"software.sslmate.com/src/certspotter/ct"
 )
 
 type List struct {
@@ -25,14 +27,14 @@ type Operator struct {
 }
 
 type Log struct {
-	Key              []byte  `json:"key"`
-	LogID            []byte  `json:"log_id"`
-	MMD              int     `json:"mmd"`
-	URL              string  `json:"url"`
-	Description      string  `json:"description"`
-	State            State   `json:"state"`
-	DNS              string  `json:"dns"`
-	LogType          LogType `json:"log_type"`
+	Key              []byte        `json:"key"`
+	LogID            ct.SHA256Hash `json:"log_id"`
+	MMD              int           `json:"mmd"`
+	URL              string        `json:"url"`
+	Description      string        `json:"description"`
+	State            State         `json:"state"`
+	DNS              string        `json:"dns"`
+	LogType          LogType       `json:"log_type"`
 	TemporalInterval *struct {
 		StartInclusive time.Time `json:"start_inclusive"`
 		EndExclusive   time.Time `json:"end_exclusive"`
@@ -69,6 +71,14 @@ type State struct {
 	} `json:"rejected"`
 }
 
+func (state *State) IsApproved() bool {
+	return state.Qualified != nil || state.Usable != nil || state.Readonly != nil
+}
+
+func (state *State) WasApprovedAt(t time.Time) bool {
+	return state.Retired != nil && t.Before(state.Retired.Timestamp)
+}
+
 type LogType string
 
 const (
diff --git a/loglist/validate.go b/loglist/validate.go
index f03d2e9..65da8e4 100644
--- a/loglist/validate.go
+++ b/loglist/validate.go
@@ -10,7 +10,6 @@
 package loglist
 
 import (
-	"bytes"
 	"crypto/sha256"
 	"fmt"
 )
@@ -35,7 +34,7 @@ func (operator *Operator) Validate() error {
 
 func (log *Log) Validate() error {
 	realLogID := sha256.Sum256(log.Key)
-	if !bytes.Equal(log.LogID, realLogID[:]) {
+	if log.LogID != realLogID {
 		return fmt.Errorf("log ID does not match log key")
 	}
 	return nil
diff --git a/scanner.go b/scanner.go
index 59295b0..1d026e4 100644
--- a/scanner.go
+++ b/scanner.go
@@ -63,7 +63,7 @@ type Scanner struct {
 
 	// Public key of the log
 	publicKey crypto.PublicKey
-	LogId     []byte
+	LogId     ct.SHA256Hash
 
 	// Client used to talk to the CT log instance
 	logClient *client.LogClient
@@ -207,12 +207,17 @@ func (s *Scanner) GetSTH() (*ct.SignedTreeHead, error) {
 			return nil, errors.New("STH signature is invalid: " + err.Error())
 		}
 	}
-	copy(latestSth.LogID[:], s.LogId)
+	latestSth.LogID = s.LogId
 	return latestSth, nil
 }
 
 func (s *Scanner) CheckConsistency(first *ct.SignedTreeHead, second *ct.SignedTreeHead) (bool, error) {
-	if first.TreeSize < second.TreeSize {
+	if first.TreeSize == 0 || second.TreeSize == 0 {
+		// RFC 6962 doesn't define how to generate a consistency proof in this case,
+		// and it doesn't matter anyways since the tree is empty.  The DigiCert logs
+		// return a 400 error if we ask for such a proof.
+		return true, nil
+	} else if first.TreeSize < second.TreeSize {
 		proof, err := s.logClient.GetConsistencyProof(int64(first.TreeSize), int64(second.TreeSize))
 		if err != nil {
 			return false, err
@@ -311,7 +316,7 @@ func (s *Scanner) Scan(startIndex int64, endIndex int64, processCert ProcessCall
 
 // Creates a new Scanner instance using |client| to talk to the log, and taking
 // configuration options from |opts|.
-func NewScanner(logUri string, logId []byte, publicKey crypto.PublicKey, opts *ScannerOptions) *Scanner {
+func NewScanner(logUri string, logId ct.SHA256Hash, publicKey crypto.PublicKey, opts *ScannerOptions) *Scanner {
 	var scanner Scanner
 	scanner.LogUri = logUri
 	scanner.LogId = logId